views:

310

answers:

4

Update2:

Thanks for the input. I have implemented the algorithm and it is available for download at SourceForge. It is my first open source project so be merciful.

Update:

I am not sure I was clear enough or everyone responding to this understands how shells consume #! type of input. A great book to look at is Advanced Unix Programming. It is sufficient to call popen and feed its standard input as demonstrated here.

Original Question:

Our scripts run in highly distributed environment with many users. Using permissions to hide them is problematic for many reasons.

Since the first line can be used to designate the "interpreter" for a script the initial line can be used to define a a decrypter

#!/bin/decryptandrun
*(&(*S&DF(*SD(F*SDJKFHSKJDFHLKJHASDJHALSKJD
SDASDJKAHSDUAS(DA(S*D&(ASDAKLSDHASD*(&A*SD&AS
ASD(*A&SD(*&AS(D*&AS(*D&A(SD&*(A*S&D(A*&DS

Given that I can write the script to encrypt and place the appropriate header I want to decrypt the script (which itself may have an interpreter line such as #!/bin/perl at the top of it) without doing anything dumb like writing it out to a temporary file. I have found some silly commercial products to do this. I think this could be accomplished in a matter of hours. Is there a well known method to do this with pipes rather than coding the system calls? I was thinking of using execvp but is it better to replace the current process or to create a child process?

+8  A: 

If your users can execute the decryptandrun program, then they can read it (and any files it needs to read such as decryption keys). So they can just extract the code to decrypt the scripts themselves.

You could work around this by making the decrtyptandrun suid. But then any bug in it could lead to the user getting root privileges (or at least privileges to the account that holds the decryption keys). So that's probably not a good idea. And of course, if you've gone to all the trouble of hiding the contents or keys of these decryption scripts by making them not readable to the user... then why can't you do the same with the contents of the scripts you're trying to hide?

Also, you can't have a #! interpreted executable as an interpreter for another #! interpreted executable.

And one of the fundamental rules of cryptography is, don't invent your own encryption algorithm (or tools) unless you're an experienced cryptanalyst.

Which leads me to wonder why you feel the need to encrypt scripts that your users will be running. Is there anything wrong with them seeing the contents of the scripts?

Brian Campbell
That is totally incorrect.
ojblass
Shells behave specially with the #! input. There is absolutely an easy way to spawn a shell in the first interpreter and feed it to the second.
ojblass
The purpose is not to hide things from the users but to forbid modification of scripts. These scripts are deployed to thousands of servers and when these scripts become unleveled (aka someone modifies it on one of the servers) without updating the chain it creates headaches. Part of our packaging and deployment process should both set the permissions (as it does now) and take additional steps because permissions don't appear to be adequate.
ojblass
I looked at your second question, and it doesn't contradict anything that I said. Your script in your second question is applying very weak encryption, that is easily cracked; when you said encrypt, I thought you meant strong encryption. You had asked if you could write decryptandrun as another #! interpreted script, which you can't do; as you do in your second question, the interpreter needs to be itself a compiled program. As long as you don't mind that your users can decrypt the script with a little bit of work, your solution is fine; I just want to make sure you know its easily cracked.
Brian Campbell
As I suspected this question was unclear as to what I was trying to do. If I lock down access to the decryption script and use a private pgp key in it I think I can give out the encrypt script with a public key to my hearts content. I don't think I want to give out the ecrypt script because I am trying to keep people from modifying things. As for proving they can't even read it I suspect that people will be able to play games by spying on memory as root but I think a completely secure solution is something a little bit beyone my abilities.
ojblass
I was more interested in the process of passing through the information. I am going to look at pgp and work on the weak encryption part next.
ojblass
A: 

All of the exec()-family functions you link to accept a filename, not a memory address. I'm not sure at all how you would go about doing what you want, i.e. "hooking" in a decryption routine and then re-directing to the decrypted script's #! interpreter.

This would require you to decrypt the script into a temporary file, and pass that filename to the exec() call, but you (very reasonably) said you didn't want to expose the script by putting it in a temporary file.

If it were possible to tell the kernel to replace a new process with an existing one in memory, you would have a path to follow, but as far as I know, it isn't. So I don't think it will be very easy to do this "chained" #! following.

unwind
Some of the calls like popen create another process with stdin and stdout nicely configured. It isn't quite replacing your process but it has the same effect.
ojblass
+3  A: 

Brian Campbell's answer has the right idea, I'll spell it out:

You need to make your script unreadable but executable by the user (jbloggs), and to make decodeandrun setuid. You could make it setuid root, but it would be much safer to make it setgid for some group decodegroup instead, and then set the script file's group to decodegroup. You need to make sure that decodegroup has both read and execute permissions on the script file and that jbloggs is not a member of this group.

Note that decodegroup needs read permission for decodeandrun to be able to read the text of the script file.

With this setup, it is then possible (on Linux at least) for jbloggs to execute the script but not to look at it. But observe that this makes the decryption process itself unnecessary -- the script file might as well be plaintext, since jbloggs can't read it.

[UPDATE: Just realised that this strategy doesn't handle the case where the encrypted contents is itself a script that starts with #!. Oh well.]

j_random_hacker
Curious to know why this was downvoted...
j_random_hacker
Per your update it is simple to feed the contents to another interpreter.http://stackoverflow.com/questions/790475/how-can-a-shell-encryption-program-use-less-user-time
ojblass
@ojblass: Hmm, I think the popen() in your decryptandrun.c on that page won't handle a (nested) #! line -- at least, running "/bin/sh < someperlscript.pl" doesn't work. Of course, you could remedy this by decoding the nested #! line yourself in that program and popen()ing the named program instead of $SHELL.
j_random_hacker
+2  A: 

You're solving the wrong problem. The problem is that you have data which you don't want your users to access, and that data's stored in a location to which the users have access. Start by attempting to fix the problem of users with more access than they require...

If you can't protect the whole script, you may want to look into just protecting the data. Move it to a separate location and encrypt it. Encrypt the data with a key only accessible by a specific ID (preferably not root), and write a small suid program to access the data. In your setuid program, do your validation of who should be running the program, and compare the name / checksum of the calling program (you can inspect the command line for the process in combination with the calling process's cwd to find the path, use lsof or the /proc filesystem) with the expected value before decrypting.

If it takes more than that, you really need to reevaluate the state of users on the system - they either have too much access or you have too little trust. :)

dannysauer