Best way to hide password in bash script?

Dear folks,

The title of my thread says mostly all of what I want to do. Basically I want to auto-ssh to a remote host, and run a program on it (VLC is just an example). I wrote a script which calls xterm and then runs expect on it. The code is as follow

#!/bin/bash

export PASS="xxxxxxx"
xterm -T VLC -e "(expect -c \"
set timeout -1
spawn ssh -X user@192.168.1.2 \\\"vlc\\\"
match_max 100000
expect \\\"*?assword:*\\\"
send -- \\\"$PASS\\r\\\"
send -- \\\"\\r\\\"
interact\") &> /dev/null"

Obviously, one can use ps to reveal what is happening

$ ps -elf | grep vlc
0 S user 11371 11370  0  80   0 -  2599 select 16:14 pts/1    00:00:00 xterm -T VLC -e (expect -c "?set timeout -1?spawn ssh -X user@192.168.1.2 \"vlc\"?match_max 100000?expect \"*?assword:*\"?send -- \"xxxxxxx\r\"?send -- \"\r\"?interact") &> /dev/null
0 S user 11372 11371  0  80   0 -  1111 wait   16:14 pts/2    00:00:00 bash -c (expect -c "?set timeout -1?spawn ssh -X user@192.168.1.2 \"vlc\"?match_max 100000?expect \"*?assword:*\"?send -- \"xxxxxxx\r\"?send -- \"\r\"?interact") &> /dev/null
0 S user 11373 11372  0  80   0 -  2982 futex_ 16:14 pts/2    00:00:00 expect -c ?set timeout -1?spawn ssh -X user@192.168.1.2 "vlc"?match_max 100000?expect "*?assword:*"?send -- "xxxxxxx\r"?send -- "\r"?interact
0 S user 11375 11373  0  80   0 -  1363 select 16:14 pts/3    00:00:00 ssh -X user@192.168.1.2 vlc
0 S user 11398 11381  0  80   0 -   834 pipe_w 16:14 pts/4    00:00:00 grep vlc

and then one can easily see the password I put in the code.

The primary reason for the code is to create a ready-to-run program for non-tech people (who have no idea about unix, about ssh, and who are not given ssh account as well), and of course I do not want them to know my password. Of course, they might be so novice that they might not know that ps command, but I just want to have some security for myself.

Any idea or advice will be greatly appreciated,

PS: the host is linux (red hat), and clients are Mac OS X (so expect and ssh can be used)

D.

Use public/private key authentication. See Google Search Results for private key authentication | The UNIX and Linux Forums

Thanks danmero for your suggestion. I want to avoid this as well, because I want to create a ready-to-run program, no need to type anything else (pass phrase for example).

D.

Whenever I post a script on my blog and include my email address, I obfuscate it with the tr command to thwart spambots. It could be used here too. If the pass was LinuxRules you could use

pass=$(echo "LinZxRZles | tr "Z" "u")'

You could also use parameter expansion to pick out the password from a longer random-looking string. None of this provides security, just a little confusion for curious novices.

Encrypting Shell Scripts - The Community's Center for Security

You can create a key pair whithout passphrase. You'll only need to give the password once for the key transfer.

Your tr'ing way is interesting fubaya. Honestly I have not heard of tr before. Anyway, that is just to protect the bash script itself, not the information produced by ps. The second way (which I thought of as well, by creating a complicated script to select the password that I want) does not help in case of ps either.

D.

---------- Post updated at 08:15 PM ---------- Previous update was at 08:12 PM ----------

shc is great, I just tried it and it works great for its purposes. Thanks for this information danmero, although again, it helps protecting the bash script, but the password still shows up when ps'ing.

D.

---------- Post updated at 08:23 PM ---------- Previous update was at 08:15 PM ----------

I am not sure if I miss something, but I am also using ssh without password in another box. Some points should be pointed out:

  • This method needs some setups on clients (creat and copy ssh key etc...)
  • This method needs passphrase each time a new shell is created on host (or first time user logs in), which is my case. Our remote host is a cluster, and each time I log on, a new shell is created.

The first point alone needs a lot of works, which is opposed to what I want to do: my program should be ready to run in any new client in the network without any complicated setup.

Thanks,

D.

---------- Post updated at 08:41 PM ---------- Previous update was at 08:23 PM ----------

I think of some solutions:

  • fool ps so that the processes showed by ps'ing (xterm, bash... ) are something else (fake name) or without parameters (such as xterm, or expect or bash only)

  • temporary disable ps (by chmod 500 `which ps` for example, which needs root privilege) or temporary link ps to a modified ps which does not show the processes I want to hide)

  • encrypted password so that ps shows only weird characters on password fields (not sure if this is doable)

Google gave me some change name or title of processes, but it seems to me that those work only for the script, not the processes called by the script (like ssh, expect etc...)

Any other ideas?

Thanks,

D.

There's no "best" way. They all suffer from exactly the same problem: Anyone who can see the code will know how to crack your password, and they'll have unlimited tries to guess your obfuscation. You might have noticed that you already had to brute-force your way around ssh's security features with 'expect' to do what you want; that's a subtle hint, in mile-high neon letters :wink: You're really not supposed to do that. It's really not a good idea. If you can pre-share a password, why can't you pre-share a key?

That is so true Corona, and I should have quoted "Best" to imply what I really wanted when posting the question: I want to learn. Few days ago I still thought that ssh was impossible to be automated, and then I learned about expect. Now I want to learn a way (if there is one) to fool ps :).

Automated ssh without my presence using my account is already not allowed if strictly considering the account policy, and of course I am not allowed to share my password. But it is really a matter of knowledge that I like the solution so much so that I want to learn more... Everything is still in development, and that I have them all in my control.

D.

Hi:

You are not going to fool ps. ps gets its process data from the kernel. You'd have to modify the kernel to lie/obfuscate, then recompile it. Or, you'd have to modify and recompile ps to lie/hide/obfuscate. And, you can forget about any LD_PRELOAD tricks since ps is a SUID binary.

Regards,
Alister

Wrong. Any C program can lie to ps and the kernel, simply by changing it's arguments. The only restriction is that the new contents may not be larger than the original if you just overwrite it (example available on request). But with just shell scripting, no, it's not possible.

Wrong. Any binary may query the kernel about running processes. On HP-UX:

$ ll `which ps`
-r-xr-xr-x   1 bin        sys         147264 Dec 17  2008 /usr/bin/ps

On Linux:

> ll `which ps`
-r-xr-xr-x 1 root root 105280 21. Sep 2007  /bin/ps

Have you tried the following:

  1. Put the script in a separate expect script file, and use your bash script as a wrapper script. From bash, call the expect file. This will prevent the expect commands from appearing in ps.

  2. Instead of hardcoding the password in the file, store the password in a separate file and protect the file so that only the authorised users can access it.

  3. Instead of using a password, encode the password algorithm instead. For example your password may be month-based, e.g. mypasswdJul2010, so put the algorithm in the code to generate it.

expect is an ugly, last-resort solution for things that won't cooperate any other way, and tends to make insecure and unreliable solutions. If you think ssh can't be automated without it, you've learned the wrong lesson; there's a much better and more secure method built into ssh itself: authorized keys. It doesn't make much sense to say "my account policy says I can't automate this; therefore I'll automate it anyway, avoid the proper way since that's not allowed, and use the least secure and most contrived way imaginable instead!" Somehow I don't think that's what their security policy had in mind.

Hi, pludi:

From the original post:

It wasn't my intention to assert that every ps implementation is suid, just those that are of interest to the original poster.

From OSX 10.4.11:

$ ls -l $(which ps)
-rwsr-xr-x   1 root  wheel  68432 Dec  7  2006 /bin/ps

From (a friend's) OSX 10.5.8:

$ ls -l $(which ps)
-rwsr-xr-x 1 root wheel 85536 Sep 10 2008 /bin/ps

I assume you mean modifying argv. Testing with the following code that I whipped up:

#include <unistd.h>

int
main(int argc, char **argv) {
    argv[1]="fake";
    sleep(30u);
    return 0;    
}

In the discussion that follows, success means that after invoking the executable with "./a.out real", ps lists it as "./a.out fake".

FAILURE: OSX 10.4.11 (suid ps)
FAILURE: Debian (kernel 2.6.18-5-686, non-suid ps)
SUCCESS: OpenBSD 4.4 (non-suid ps).

(Yeah, I have a lot of old machines lying around with outdated os installs ;))

Furthermore, in the BSD-ish cases (at least, perhaps others), the struct with the process info contains the original argv in addition to the current argv (which may differ as when deliberately modified by the process itself).

If you were referring to a different type of argv manipulation, or something altogether different, or if there's an error in my C code, I would appreciate the enlightenment.

Regards,
Alister

Yes, I was referring to argv[] manipulation. Using memset() on argv[1]

  • works with Linux 2.6.22 (OpenSuSE) using a non-suid ps, and with ps run as root
  • does not work on HP-UX 11.31 using a non-suid ps
  • does not work on FreeBSD 8.0 using a non-suid ps (as root)
  • does not work on OpenSolaris 2009.06 using a non-suid ps

Seems like it's not dependent on the permissions of ps, but rather on the system.

Hey, pludi:

memset() on the string pointed to by argv[1] works on osx 10.4.11, where modifying the pointer in argv[1] directly (as my code was doing) failed.

Incidentally, as an unprivileged user, copying the suid ps binary so that it is no longer suid seems to work fine. I only tested a few options, however, so it's possible there's a privileged code path that I did not visit, just waiting to blow things up, but if collecting the process info did not require privilege -- I took a brief peek at Apple's ps.c, and it's using sysctl to gather the info; so, not surprising that privilige is not required for that step -- I don't see what would.

Regards,
Alister

Hi Corona, I think you got it wrong. I did mention that I knew authorized keys already (and I have been using it quite a while), but in my opinion it is not automation as expect (yes, I thought it wrong you may think), and that I did not hear of expect before.

Anyway, let us try not to diverge the main question of my post: how to fool ps or hide password in my script.

Thanks,

D.

---------- Post updated at 06:08 PM ---------- Previous update was at 06:02 PM ----------

Yes, I did. I tried it before combining my scripts to the script I posted. And the password still showed with ps.

I thought of this already, and it actually is a solution that a lot people recommend instead of passing password through arguments. But the permission of the file can be easily change by root, hence can be seen easily right?

And finally the decoded pass will be still passed to the argument of ssh or expect, or we can pass the encoded one?

Thanks,

D.

---------- Post updated at 06:14 PM ---------- Previous update was at 06:08 PM ----------

I found a post describing similar method, but I have no idea how to apply this to change (or fake) the name of the built-in process (such as expect or ssh) given its pid. If somehow ssh (or its argument) can be changed (or faked) then it is the solution, I think.

D.

---------- Post updated at 06:18 PM ---------- Previous update was at 06:14 PM ----------

Hi Alister,

I tried your code (modifying argv[1]) and it did not work on 10.6.3 either. Do you mean modifying memset() can change output of ps for built-in processes (xterm, expect, ssh, bash...) in my first post?

Thanks,

D.

In what manner is it worse do you think? It may not be easy to explain to your customers or superiors but it is fully automatic and secure. Your method is not and cannot be made secure. The inventor of ssh knew people would try to butcher in stored plaintext passswords anyway and designed it to prevent that.

Not until you understand that the reason you have to use expect at all is because you're brute-forcing an insecure solution ssh was expressly designed to not just prevent but make obsolete.

I meant that the following works on 10.4.11, for clearing out all argument strings pointed to from argv (which usually show up in ps output):

#include <string.h>
#include <unistd.h>

int
main(int argc, char **argv) {
    while (--argc) {
        memset(argv[argc], 0, strlen(argv[argc]));
    }
    sleep(30u);
    return 0;
}

You cannot do this from a shell script, though. Also, even if you could, there is a race condition present. The args could be visible from ps if ps is run at just the right time and the kernel's process scheduler conspires against you.

As Corona has made clear, there is no secure way to accomplish what you're trying to do. The best you can manage is a few obstacles to keep the undetermined at bay.

Regards,
Alister

Got it. Thanks Alister.

D.