[comp.unix.questions] Destroying arguments

guy%gorodish@Sun.COM (Guy Harris) (05/18/87)

This really isn't a C question at all, it's a UNIX question, so I'll
redirect it where it should have been sent in the first place.

> Newsgroups: comp.lang.c
> Distribution: comp.unix.questions

In fact, it looks like the orignal poster may have tried directing it
there, but goofed.

> Alas, this wonderfully simple technique simply does not work with
> the 4BSD `ps', which reads the strings themselves, not the pointers
> thereto.

As do most other versions of "ps".  The 3B2 version of "ps" does it
differently; the kernel stashes a string away in the U area that
contains the original arguments used to invoke the program.  (I have
no idea whether this was done because somebody thought this was the
right way to do things, or because they couldn't figure out how to
snarf the arguments up on a machine whose stack grows upward in
memory....)  On this machine, you *can't* smash your argument list.
Period.

The answer is "forget it".  In general, it can't be done.  Some
versions of "crypt" write '\0' all over the password it is handed,
which means it'll smash the argument if that was where the key
appeared, but 1) this won't work on a system that works the way 3B2
UNIX does and 2) still leaves a window in which the argument can be
seen.

We won't mention that it also leaves the encryption key on your
screen when you type the command, or that using "crypt" in this
fashion in a *script* is a very bad mistake as it leaves the
encryption key in a file.  In short, the answer is "always get
encryption keys from the user, and turn off echoing when you do so".

jgy@hropus.UUCP (John Young) (05/19/87)

Guy Harris says:

> This really isn't a C question at all, it's a UNIX question, so I'll
> redirect it where it should have been sent in the first place.
> .........
> As do most other versions of "ps".  The 3B2 version of "ps" does it
> differently; the kernel stashes a string away in the U area that
> contains the original arguments used to invoke the program.  (I have
> no idea whether this was done because somebody thought this was the
> right way to do things, or because they couldn't figure out how to
> snarf the arguments up on a machine whose stack grows upward in
> memory....)  On this machine, you *can't* smash your argument list.
> Period.
> ......

Not true. It depends on which UNIX your running on your 3B2:

a)  On a SWAPPING system a 'ps -l' will show you
the u.u_comm field which will give you JUST the first argument (normally
the program name) of the process. This can not be changed by normal
methods from inside the process.  'ps -f' will show you what's
really pointed to by argv[0] which can be changed.

b)	On a PAGING system ONLY the first 40 bytes of arguments are copied
into the saftey of the user block (ps may not show you much more
than this anyway) so if you want arguments passed to you with some
secrecy just put them futher down the line.

levy@ttrdc.UUCP (05/20/87)

In article <19131@sun.uucp>, guy%gorodish@Sun.COM (Guy Harris) writes:
> The 3B2 version of "ps" does it
> differently; the kernel stashes a string away in the U area that
> contains the original arguments used to invoke the program.  (I have
> no idea whether this was done because somebody thought this was the
> right way to do things, or because they couldn't figure out how to
> snarf the arguments up on a machine whose stack grows upward in
> memory....)  On this machine, you *can't* smash your argument list.
> Period.
> The answer is "forget it".  In general, it can't be done.

This may well be true on some machines and implementations, but don't
unjustly blacken the 3B2, please.  I tried this code on a 3B2/400
running System V Release 2.0.4 swapping:

main(argc,argv)
int argc;
char *argv[];
{
	int i;
	char *c;
	void perror();

	for (i=0; i<argc; i++)
		for (c=argv[i]; *c; c++)
			*c = 'X';	/* stomp all over argv */

	if (fork() == 0) {
		(void) execl("/bin/ps", "ps", "-f", (char *) 0);
		perror("Can't execute /bin/ps");
	}
	else
		(void) wait((int *)0);	/* hang around till ps finishes */

	return 0;
}

The results (call the program stompargv):

$ stompargv arg_one arg_two arg_three
     UID   PID  PPID  C    STIME TTY      TIME COMMAND
    levy  1603     1  0 16:30:04 tty11    0:03 -sh
    levy  1605  1603  7 18:40:18 tty11    0:00 XXXXXXXXX XXXXXXX XXXXXXX XXXXXXXXX
    levy  1606  1605 44 18:40:18 tty11    0:00 ps -f

(Could someone please try this on SVR3 3B2 and tell me if it works the
same way?)
-- 
|------------dan levy------------|  Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa,
|         an engihacker @        |		vax135}!ttrdc!ttrda!levy
|    at&t data systems division  |  Disclaimer:  try datclaimer.
|--------skokie, illinois--------|

guy%gorodish@Sun.COM (Guy Harris) (05/21/87)

> > The 3B2 version of "ps" does it
> > differently; the kernel stashes a string away in the U area that
> > contains the original arguments used to invoke the program.  (I have
> > no idea whether this was done because somebody thought this was the
> > right way to do things, or because they couldn't figure out how to
> > snarf the arguments up on a machine whose stack grows upward in
> > memory....)  On this machine, you *can't* smash your argument list.
> > Period.
> 
> This may well be true on some machines and implementations, but don't
> unjustly blacken the 3B2, please.  I tried this code on a 3B2/400
> running System V Release 2.0.4 swapping:
> 
> (Could someone please try this on SVR3 3B2 and tell me if it works the
> same way?)

I just did.  It doesn't work the same way.  If I do

	$ ./stomp_argv

the "ps" it runs says that a program named "./stomp_argv" is running
(and no, it is not picking up "u.u_comm"; if it were, it would have
said "[ stomp_argv ]", not "./stomp_argv").  In other words, on a 3B2
running S5R3, you can't hide your argument list from "ps"; my
assertions stand.

Maybe the swapping release didn't have this hack to stuff the
argument list into the U area - there is a comment in the S5R3 "ps"
that says

	/* no u_psargs on 3b5, look in user stack */

and I'm not certain the 3B5 had a paging release, so this may be the
case.  One is tempted to ask, however, what is so difficult about
this?  The code to snarf this stuff from the user area has

	#if vax || u3b || u3b2

around it.  It is certainly not impossible to get the argument list
from the process' stack on a paging VAX.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

kimcm@olamb.UUCP (Kim Chr. Madsen) (05/21/87)

In article <19131@sun.uucp>, guy%gorodish@Sun.COM (Guy Harris) writes:
> 
> As do most other versions of "ps".  The 3B2 version of "ps" does it
> differently; the kernel stashes a string away in the U area that
> contains the original arguments used to invoke the program.  (I have
> no idea whether this was done because somebody thought this was the
> right way to do things, or because they couldn't figure out how to
> snarf the arguments up on a machine whose stack grows upward in
> memory....)  On this machine, you *can't* smash your argument list.
> Period.

(remove 'period)

It can be done on the 3B2 w/ standard ps(1), I've used it!

(set 'period)

I've used the argv[1] to tell some status information about the program,
which ran in the background and just doing a "ps -fp <pid>" returned the
info. nicely.

For information I ran this under UNIX V 2.0.4 on a 3B2-400. And don't tell
me it's non-portable it wasn't meant to be !

						Kim Chr. Madsen

guy%gorodish@Sun.COM (Guy Harris) (05/22/87)

> b)	On a PAGING system ONLY the first 40 bytes of arguments are copied
> into the saftey of the user block (ps may not show you much more
> than this anyway) so if you want arguments passed to you with some
> secrecy just put them futher down the line.

Funny, the source we have says 80 bytes....

And as for "put them further down the line", *who* is doing the
putting?  If a program wants arguments passed to it with some
secrecy, it has to politely request that whoever or whatever
*invokes* it should ensure that the argument in question appear more
than 80 characters into the argument list.  A program (or programmer)
that made such a request of me would be likely to get a rude gesture
in response.

The point is that you can't, in general, ensure that arguments passed
to a program can't be seen by somebody doing a "ps"; you definitely
can't do so on a 3B2 running any reasonably recent version of UNIX
for the 3B2.  Even on machines where you can smash the argument list,
there is a small chance that "ps" will suck up the argument list
before the program gets a chance to smash it.  In other words, don't
put encryption keys on the command line if you can possibly avoid it.

(Another question that comes to mind is "why did they waste 80 bytes
of U-area space with this stuff?"  It's not as if you can't fetch an
argument list on a paged machine, and the ability to get the command
line used to run a program does not seem to be of sufficient
importance that stuffing it in the U page buys you anything.  You
*still* aren't guaranteed that you can get it, since the process may
get swapped in or out between the point at which "ps" gets the
process table entry for the process and the point at which it grabs
the U area.)
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

jgy@hropus.UUCP (John Young) (05/22/87)

>> b)	On a PAGING system ONLY the first 40 bytes of arguments are copied
>> into the saftey of the user block (ps may not show you much more
>> than this anyway) so if you want arguments passed to you with some
>> secrecy just put them futher down the line.
>
>Funny, the source we have says 80 bytes....
>
>And as for "put them further down the line", *who* is doing the
>putting?  If a program wants arguments passed to it with some
>secrecy, it has to politely request that whoever or whatever
>*invokes* it should ensure that the argument in question appear more
>than 80 characters into the argument list.  A program (or programmer)
>that made such a request of me would be likely to get a rude gesture
>in response.
>

Seems to me that if you wanted to pass an argument "safely" to a
process, and knew how to, you would do it the way
I suggested.  If you do not want secrecy or wanted to spite
yourself for the inconvenience
(maybe you don't have auto repeat on your keyboard) then you would not.
Or maybe you'd just wall the argument around the system.


	jgy

guy%gorodish@Sun.COM (Guy Harris) (05/23/87)

> Seems to me that if you wanted to pass an argument "safely" to a
> process, and knew how to, you would do it the way
> I suggested.

If I wanted to pass a password safely to a program, I'd have the
program ask me for the password and I'd type it in!  If the program
insisted that I pass it as an argument, I'd simply declare the
program unacceptable.  I wouldn't do it the way you suggest at all;
requiring the user to type 40, or 80, or however many Xes is simply
silly.

> If you do not want secrecy or wanted to spite yourself for the inconvenience
> (maybe you don't have auto repeat on your keyboard) then you would not.

Great.  Now, in order to use some program safely, you just require
1) that the user know the maximum number of characters the "ps" on
the system will print (of course, this assumes that "ps" *imposes*
such a maximum; the maximum might very well be NCARGS) and 2) that
the user type this many Xes.

Sorry, that user interface design gets an F.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

ado@elsie.UUCP (Arthur David Olson) (05/23/87)

> ...requiring the user to type 40, or 80, or however many Xes is simply
> silly.

Seems like you could reduce (but not eliminate) the window of vulnerability by
having the program reexecute itself this way:

#define XX "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

main(argc, argv)
char *	argv[];
{
	if (strcmp(argv[0], XX) != 0) {
		argv[0] = XX;
		execv(MYSELF, argv);
		perror(MYSELF);
	}
}

Doing this would not require the user to type in Xes.
-- 
	UUCP: ..seismo!elsie!ado	   ARPA: elsie!ado@seismo.CSS.GOV
	     Elsie and Ado are trademarks of Borden, Inc. and Ampex.x

dhesi@bsu-cs.UUCP (Rahul Dhesi) (05/23/87)

In article <19590@sun.uucp> guy%gorodish@Sun.COM (Guy Harris) writes:
>If I wanted to pass a password safely to a program, I'd have the
>program ask me for the password and I'd type it in!

I once wanted to write a shell script that would invoke crypt.  The
shell script prompted for the password, then later invoked crypt and
supplied the password to it.

The problem I ran into was that there seemed to be no way (at that
time, on a 4.2BSD system; I haven't tried recently under 4.3BSD) to let
crypt read the password from the script.  The only way seemed to be
either to let crypt read it from the terminal or get it from a
parameter.  Reading from the terminal was not practical because I was
using a script that encrypted a large number of files using the same
password, and typing in the password multiple times is a pain.
Supplying the password to crypt as a parameter was obviously
unacceptable.

I eventually gave up trying to do it from a shell script.  It would
probably have been necessary to write an executable program.
-- 
Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

levy@ttrdc.UUCP (Daniel R. Levy) (05/24/87)

In article <19364@sun.uucp>, guy%gorodish@Sun.COM (Guy Harris) writes:
< < < The 3B2 version of "ps" does it
< < < differently; the kernel stashes a string away in the U area that
< < < contains the original arguments used to invoke the program. 
< < < On this machine, you *can't* smash your argument list.
         ^^^^^^^^^^^^ !!!!
< < < Period.

[My refutation program behaves differently on a 3B2 running SVR2 swapping.
Guy tries it on SVR3 3B2 and finds ps still shows the argv at time of
invocation.]

< In other words, on a 3B2
< running S5R3, you can't hide your argument list from "ps"; my
  ^^^^^^^^^^^^ !!!!
< assertions stand.

They fail when applied to the 3B2 as a MACHINE.  Sorry, Guy, my example
to the contrary disproves your assertion AS YOU STATED IT IN YOUR ORIGINAL
COMMENTS.  Now if you wish to comment upon SPECIFIC OPERATING SYSTEM VERSION
AND MACHINE COMBINATIONS, be my guest.
-- 
|------------dan levy------------|  Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa,
|         an engihacker @        |		vax135}!ttrdc!ttrda!levy
|    at&t data systems division  |  Disclaimer:  try datclaimer.
|--------skokie, illinois--------|

guy%gorodish@Sun.COM (Guy Harris) (05/24/87)

> The problem I ran into was that there seemed to be no way (at that
> time, on a 4.2BSD system; I haven't tried recently under 4.3BSD) to let
> crypt read the password from the script.

All the versions of "crypt" I know of use "getpass" to read the
encryption key.  This means that the only ways to get any of them to
read the key from something other than the user's terminal would be:

	1) detach "crypt" from the terminal, in which case *some*
	   versions would read from the standard input.  The System V
	   version doesn't; the V7, 4BSD, and S3 versions do.

	2) somehow attach the "crypt" process to a pseudo-terminal so
	   that its "/dev/tty" is that pseudo-terminal, and feed it
	   the key through the pseudo-terminal.

The S5R3 version permits you to specify the encryption key with an
environment variable, which is more secure than specifying it as an
argument iff you have no version of "ps" that can look at environment
variables.  The one distributed with S5 doesn't, but that doesn't
mean you can't make one that does, and the one distributed with 4BSD
definitely does.

If you pass the key as an argument, the S5R3 version stuffs the key
into an environment variable and "exec"s itself with no arguments, in
an attempt to hide the key away.  (Null and void with a "ps" that
dumps the environment, of course.)  (BTW, the S5R3 manual lies; the
environment variable is named "CrYpTkEy", not "CRYPTKEY".)

Then again, I'd be a tad chary of typing such an encryption key to a
shell, unless I could make sure nobody was looking over my shoulder.
If at a printing terminal, you'd also have to safely discard the
printout, and if at a CRT terminal you'd have to make sure nobody
could somehow coax your terminal into transmitting the screen's
contents and read what it transmitted.

For that matter, you'd also have to make sure nobody had a
"crypt"-cracker handy.  See "File Security and the UNIX System Crypt
Command", J. A. Reeds and P. J. Weinberger, AT&T Bell Laboratories
Technical Journal, October 1984, Vol. 63, No. 8, Part 2.

> Reading from the terminal was not practical because I was
> using a script that encrypted a large number of files using the same
> password, and typing in the password multiple times is a pain.

The number of files would have to be *very* large for "a pain" to
imply "impractical".
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

guy%gorodish@Sun.COM (Guy Harris) (05/24/87)

> Seems like you could reduce (but not eliminate) the window of vulnerability
> by having the program reexecute itself this way:

Yup - a much better suggestion than the one offered by the guy who
suggested using the repeat key to type lots of letters, or perhaps
writing your own wrapper around a utility putatively provided in
usable form; this one puts the burden on the author of the encryption
program, not its user, which is as it should be.  However, this still
relies on having a "ps" that won't divulge more than N characters of
the argument list.

There is probably some appropriate value of N for most UNIX
implementations, but I don't know that 1) all implementations would
have such a limit or 2) for those implementations that do, that this
value of N would be smaller than NCARGS by a sufficient amount to
include an encryption key.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

jjw@celerity.UUCP (Jim ) (05/25/87)

If you really want secure arguments then they shouldn't be on the command line
at all.  Put them in a file with read permission denied to all but the owner
and pass the name of the file to the program.

Anyone with a Berkeley based system should check out what happens with
	"ps axww"
(that's two w's) to see how secure the idea of pushing the arguments down
the list are.

					-- J. J. Whelan
					   Celerity Computing

hope@gatech.UUCP (05/26/87)

In article <199@celerity.UUCP> jjw@celerity.UUCP (Jim (JJ) Whelan) writes:
>Anyone with a Berkeley based system should check out what happens with
>	"ps axww"

If you _really_ want secure (i.e., unreadable) arguments then fill up your
environment with _lots_ of junk.  Twenty-five or so environment variables set
to about 70 characters each will keep 'w' and 'ps' from displaying them, even
with ps axwwwwwww.

This works on BSD systems, although different implementations might "require"
you to have much more in your environment.

I don't think it works on SysV.

-- 
Theodore Hope
School of Information & Computer Science, Georgia Tech, Atlanta GA 30332
CSNet: Hope@gatech		ARPA: Hope@Gatech.EDU
uucp:	...!{akgua,decvax,hplabs,ihnp4,linus,seismo,ulysses}!gatech!hope

pdg@ihdev.ATT.COM (Joe Isuzu) (05/26/87)

In article <15605@gatech.gatech.edu> hope@gatech.UUCP (Theodore Hope @ LEGOLAND) writes:
>In article <199@celerity.UUCP> jjw@celerity.UUCP (Jim (JJ) Whelan) writes:
>>Anyone with a Berkeley based system should check out what happens with
>>	"ps axww"
>If you _really_ want secure (i.e., unreadable) arguments then fill up your
>environment with _lots_ of junk.  Twenty-five or so environment variables set
>to about 70 characters each will keep 'w' and 'ps' from displaying them, even
>with ps axwwwwwww.
>This works on BSD systems, although different implementations might "require"
>you to have much more in your environment.
>I don't think it works on SysV.

Actually it does.  System V ps does not have an option for displaying
the environment, though.  It isn't difficult to find, however, and
anyone with the slightest kernel hacking experience could do such an
addition.  I don't recommend the above method (filling the
environment).  Remember this is overhead on process creation.  There
are also certain programs (none I know under Berkeley, but a few under
system V) that will break (read core dump) if the environment is too
large.  (I still would maintain that that is a bug which should be
fixed - but that'll take a long time).  If you want to fix this on a
system wide basis, and have a reasonably secure system (ps, w setgid
to kmem, and mem, kmem unreadable by all), why not just fix up ps and
w to display u.u_comm, instead of looking for all the args?  And turn
off the environment option while you are there (this is also good for
seeing what directory someone is in as csh and ksh add this into the
environment).  
-- 

Paul Guthrie				"Another day, another Jaguar"
ihnp4!ihdev!pdg				    -- Pat Sajak

jjw@celerity.UUCP (05/27/87)

In article <15605@gatech.gatech.edu> hope@gatech.UUCP (Theodore Hope @ LEGOLAND) writes:

>If you _really_ want secure (i.e., unreadable) arguments then fill up your
>environment with _lots_ of junk.  Twenty-five or so environment variables set
>to about 70 characters each will keep 'w' and 'ps' from displaying them, even
>with ps axwwwwwww.
>
>This works on BSD systems, although different implementations might "require"
>you to have much more in your environment.
>
>I don't think it works on SysV.

Also doesn't work on Celerity BSD systems which can find the command arguments
regardless of environment variables.

gwyn@brl-smoke.UUCP (05/27/87)

In article <19590@sun.uucp> guy%gorodish@Sun.COM (Guy Harris) writes:
>If I wanted to pass a password safely to a program, I'd have the
>program ask me for the password and I'd type it in!

There's even a library routine, getpass(), to make this easy to do.

rml@hpfcdc.HP.COM (Bob Lenk) (05/29/87)

> > Seems like you could reduce (but not eliminate) the window of vulnerability
> > by having the program reexecute itself this way:
> 
>                                               ... However, this still
> relies on having a "ps" that won't divulge more than N characters of
> the argument list.
> 
> There is probably some appropriate value of N for most UNIX
> implementations, but...

If you really want to use this type of an approach, the initial
invocation can pipe() and write the password to the pipe, and the second
(re-exec'd) invocation can read it back.  I suppose this somewhat
increases the window when when first invocation runs.

		Bob Lenk
		{ihnp4, hplabs}!hpfcla!rml