88

Is it possible to execute a script if there is no permission to read it? In root mode, I made a script and I want the other user to execute this script but not read it. I did chmod to forbid read and write but allow execute, however in user mode, I saw the message that says: permission denied.

1

9 Answers 9

82

The issue is that the script is not what is running, but the interpreter (bash, perl, python, etc.). And the interpreter needs to read the script. This is different from a "regular" program, like ls, in that the program is loaded directly into the kernel, as the interpreter would. Since the kernel itself is reading program file, it doesn't need to worry about read access. The interpreter needs to read the script file, as a normal file would need to be read.

3
  • 2
    Yes but in his case is there a solution? Commented Mar 22, 2012 at 10:53
  • 15
    One possibility would be to have a simple C program that embeds the script and calls the interpreter explicitly. A C program does not need to have read permissions to execute.
    – Arcege
    Commented Mar 22, 2012 at 12:04
  • 4
    Strictly speaking, the kernel does not discriminate in this case and it will in fact run the shell (the same way as if the executable file was a binary). However, the shell itself will immediately crash because it can't read the input file (the script file content).
    – knaperek
    Commented Mar 21, 2019 at 15:58
46

This is possible only for binaries.

$ chown foo:foo bar
$ chmod 701 bar

As the unprivileged user:

$ ls -lha bar
-rwx-----x 1 foo foo 7.0K 2012-03-15 03:06 bar

$ cat bar
cat: bar: Permission denied

$ ./bar
baz

Now, here is the kicker. While the file is unreadable by conventional means, you can't actually prevent reading the file. This is actually a challenge on http://smashthestack.org/ (level 13). There is a well known utility called hktrace that allows you to read the file using ptrace.

4
  • Very interesting (hktrace).
    – fthinker
    Commented Mar 16, 2012 at 5:01
  • 1
    is it possible to convert shell script to binary format?
    – ashim
    Commented Apr 4, 2012 at 22:57
  • 10
    Actually, you can prevent it, I think. Current linux kernel versions set the process to non-dumpable, meaning no normal user can ptrace it anymore, if the user is not allowed to read the binary.
    – thejh
    Commented May 15, 2014 at 11:49
  • 1
    On Linux there's also a security module called yama which has the sole purpose of restricting traces, so you'll also have to keep an eye out for that. As far as I know the default settings are pretty lax tho, if not disabled.
    – Leo S.
    Commented Aug 10, 2022 at 21:41
8

There's a half truth to the previous statements. You can setup a script so that it's not readable by the user, but still executable. The process is a little drawn out, but it's doable by making an exception in /etc/sudoer so that the user can run the script as yourself temporarily without being prompted for a password. This method: - gets around the setuid patch for other distros. - allows you to give elevated permissions temporarily for a specific script without giving the user sudo rights to everything.

Follow the instruction on this post: File permission execute only

1
  • This is the best solution on this page.
    – Luke
    Commented Jun 9, 2021 at 12:55
6

This is not possible, at least on Linux (other Unices might allow it); think about it, when you run the script, the shell needs to read it in order to know what to do.

2
  • 4
    It certainly is possible; OpenBSD allows any script to be executed without read permission. Under the hood it does this by creating a duplicate file descriptor for the interpreter to use.
    – eradman
    Commented Apr 12, 2018 at 12:36
  • @eradman I've put that (together with an example, explanation, and some remarks of mine) into an answer.
    – user313992
    Commented Oct 15, 2019 at 7:03
2

You can, I think, do this with setuid.

Except that you can't because most distros (apparently) have setuid disabled because it's a massive security hole. It's disabled on mine, so I don't actually know that this answer will work, I'm posting it anyway because I think it should.

Anyway, if I wanted to do what you wanted to do--and I had a distro with setuid enabled for scripts--I would do something like:

$ chmod 700 myscript
$ cat > myscript-nonroot
#!/bin/sh
bash myscript
^D
$ sudo chown root:root myscript-nonroot
$ sudo chmod 4755 myscript-nonroot # make SURE this isn't world-writable!

Which is to say I would write another script whose sole purpose is to call the root-read-only script, change it to be owned by root, and give it the setuid permission. (Along with attendant non-writable status by everyone else.)

Since the myscript-nonroot function is readable by everyone it can be read and executed, and by the time you get two the line where you actually execute your script (bash myscript) it is being run as root (or whoever else you want, the exact user doesn't matter, as long as the wrapper file is owned by the same user.)

5
  • What does 4755 mean? I'm new to this, so I would like to know what it means. I understand the 755 part. Thanks
    – Kevdog777
    Commented Jul 18, 2012 at 8:17
  • 3
    the 4 sets the setuid bit. See the Modes section in the chmod man page on manpagez. Commented Jul 18, 2012 at 21:16
  • Ok, I still don't quite understand, but it did take me a while to understand the 755 bit.
    – Kevdog777
    Commented Jul 19, 2012 at 7:16
  • yep, actually chmod 755 is same than 0775 octal. there's a lot of confusion around that.. This page (manpagez.com/man/1/chmod) has an awfull and unneded horizontal scroll that i can't understand...
    – m3nda
    Commented Apr 22, 2017 at 5:41
  • 1
    setuid isn't disabled on any modern major Linux distro (or any modern major Linux distro that was active 8 years ago when this answer was written either); it's disabled for scripts only on modern kernels, but programs like sudo, su, etc. wouldn't work without setuid. Commented Sep 30, 2020 at 18:20
2

On this situation I used sudo with a NOPASSWD option so the users can run the script without being able to read it.

1

It works on OpenBSD

As already mentioned in a comment by @eradman, this is possible on OpenBSD.

As root:

hzy# cat <<'EOT' >/tmp/foo; chmod 001 /tmp/foo
#! /bin/sh
: this is secret
echo done
EOT

As a regular user:

hzy$ cat /tmp/foo
cat: /tmp/foo: Permission denied
hzy$ /tmp/foo
done

That works by passing /dev/fd/3 (or whatever the open fd to the script is) to the interpreter. That trick would not work on Linux, where /dev/fd/N are not special character devices which return a dup(2) of the fd when opened, but "magic" symlinks to the original file/dentry, which open the file from scratch [1]. It could be implemented in Free/NetBSD or Solaris ...

But it's not what it's cracked up to be

Basically giving the x (execute) permission means also giving the r (read) permission on any file which has a shebang [2]:

hzy$ cat /tmp/foo
cat: /tmp/foo: Permission denied
hzy$ ktrace -ti /tmp/foo
done
hzy$ kdump | tail -n8
 70154 sh       GIO   fd 10 read 38 bytes
       "#! /bin/sh
        : this is secret
        echo done
       "
 70154 sh       GIO   fd 1 wrote 5 bytes
       "done

ktrace is not the only way; if the interpreter is dynamically linked executable like perl or python, a LD_PRELOADed hack which overrides the read(2) function could be used instead.

And no, making it setuid will not prevent a regular user from seeing its content; she could simply run it under ptrace(2), which will cause the setuid bits to be ignored:

As root:

hzyS# cat <<'EOT' >/tmp/bar; chmod 4001 /tmp/bar
#! /bin/sh
: this is secret
id
EOT

As a regular user:

hzyS$ ktrace -ti /tmp/bar
uid=1001(duns) euid=0(root) gid=1001(duns) groups=1001(duns)
hzyS$ kdump
    ... nothing, the kernel disabled the ktrace ...
hzyS$ cc -Wall -xc - -o pt <<'EOT'
#include <unistd.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>

int main(int ac, char **av){
        int s; pid_t pid;
        if((pid = fork()) == 0){
                ptrace(PT_TRACE_ME, 0, 0, 0);
                execvp(av[1], av + 1);
        }
        while(wait(&s) > 0 && WIFSTOPPED(s)){
                s = WSTOPSIG(s);
                ptrace(PT_CONTINUE, pid, (caddr_t)1, s == SIGTRAP ? 0 : s);
        }
}
EOT
hzyS$ ./pt ktrace -ti /tmp/bar
uid=1001(duns) gid=1001(duns) groups=1001(duns)
hzyS$ kdump | tail -5
 29543 sh       GIO   fd 10 read 31 bytes
       "#! /bin/sh
        : this is secret
        id
       "

(sorry if this is not the most straighforward way to demonstrate it)

[1] this could be emulated on Linux by using binfmt_misc, but the interpreter will have to be modified, or a wrapper will have to be used; see the last part of this answer for an example deliberately made ridiculously insecure.

[2] or in general, any file which will not cause execve() to return ENOEXEC.

-2

Yes, if you are root user, you can execute file without read permission

# echo "echo hello" > test
# chmod 100 test
# ll test
---x------ 1 root root 10 Nov 29 12:13 test
# ./test
hello

But if you log in with any other user, you cannot execute this file

$ ./test
-bash: ./test: Permission denied
1
  • 5
    This doesn't really answer the question, cause root can still read the file even without permission.
    – wjandrea
    Commented Mar 22, 2018 at 20:12
-2

To make your scripts unreadable yet executable, you have 3 major options:

First Option

Use the openssl command to manually encrypt it. And in the future, when you want to run the script, you'll have to run openssl manually again, and provide the password to decrypt.

Encryption with openssl:

cat yourscript.sh | openssl aes-128-cbc -a -salt -k yourpassword > yourscript.enc

Decryption with openssl:

cat yourscript.enc | openssl aes-128-cbc -a -d -salt -k yourpassword > yourscript.dec

yourscript.dec will be the same as your original script yourscript.sh

Second Option

Use a site like www.Enscryption.com to automatically encrypt your script and make the encrypted version of the script executable. This site uses both the encryption capabilities of openssl and some other obfuscation methods to make it quite difficult for intruders to pry into your script or to unveil secrets you want hidden. With this site, you can encrypt shell scripts and command line perl, python, ruby scripts. I think php as well.

Third Option

Use a tool such as shc. Looks like it hasn't been updated since 2012. but I have used it in the past. You have to compile your script for each OS on which you wish to use it on, if the OS is different from the one you used to compile it.

Summary:

If hiding your code is of great importance to you, relying only on permissions and ownership is going to help you, as anyone with root can get to it. That's just a fact. What you can do, if you really want to do prevent unauthorized viewing of your code by anyone is to write a script around the openssl command. Make it so that, before the script runs, it'll prompt for a password AND after password is given, it'll run the script without writing it to a temp file. If this sounds like too much work then options 2 and 3 should be sufficient for your purposes.

2
  • The "shc" link also points to the one page from before. Are you by any chance affiliated with this page/service?
    – phk
    Commented Jun 8, 2016 at 22:12
  • 4
    If you cipher your script it will not be able to run without key. If you sent the key to a user to run it, will be able to see it's content. This anwers is so stupid...
    – m3nda
    Commented Apr 22, 2017 at 6:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .