Bugtraq mailing list archives

Re: For example ...


From: nlawson () statler csc calpoly edu (Nathan Lawson)
Date: Fri, 2 Dec 1994 00:13:05 -0800 (PST)


Here's what I would consider a "real" bug report.  I don't have a fix handy,
other than suggesting that one overwrite Sun's /bin/mail crock with a
militrary-grade erase pattern, burn any backup tapes it is on, and replace it
with something like the bsd44 "mail.local" or "deliver" or similar things that
are much better written, using mkstemp(), fchown/fchmod, and such.

Actually, overwriting is unnecessary.  Just take off the suid bit and link
/bin/mail to a more useful mail reader like /usr/ucb/mail for those diehards
that actually use binmail as a reader.  Change the Mlocal line in 
/etc/sendmail.cf to point to a new local agent like (as you said) mail.local
or procmail.  Mkstemp() was suggested by another a while back, yet the real
problem lies in binmail using an fopen() for the temp and lock files.

This should get you thinking [again!] about *anything* that runs setuid
and calls the old mktemp.

Hear hear.  Probably a losing battle, but it gets my vote.  The *best* way
would be for the program to use setruid() to switch euid and ruid at the 
very beginning of the file and only switch back when it NEEDS the privileges
instead of doing everything with privileges and dropping them when the 
designers thought they didn't need them.

/* some binmails use /tmp/maXXXXX, not maaXXXX.  Try taking a copy of
   /bin/mail and tracing it, to see what's actually using. */

I had heard of this, but only had an Ultrix machine to test it on.

      strcpy (target,"/tmp/maa");  /* either this or "/tmp/ma" */
              /* General format for binmail temp names */

Actually, what happens is that Ultrix uses "/tmp/maXXXXX", but mktemp 
initializes the name to be the pid, plus a character.  The first one is
"a1234" the second "b1234", ad nauseum.  It's just another way to make it
unique.

              if ((pid = fork())==0) { 
                 sleep (1);
                 nice (19);  /* Increase our chances and ... */

I still don't know if a parent process has any control over the niceness of
its children if they are privileged and the parent is not.  This was the only
reason I could think why 8lgm never used any nice() calls in their scripts.

Explanation of improved strategy, so we all understand this...

Mail starts off very early by doing a mktemp() to make a temp file name.
We meanwhile have ponied up this same name because we know the pid of the
process and how mktemp() works.  We both arrive at /tmp/ma01234, say.
                                                            ^
                                                        Again, usually a letter

Mail tries to delete /tmp/ma01234 in case it was somehow there already --
probably a primitive attempt to evade symlink-hacks.  [Can *you* envision
a day when it went blindly ahead and opened the file?  I can...]

Not as if this is any better.

Meanwhile, we are spinning in a loop, unlinking and symlinking and unlinking
and symlinking to our real target, waiting for unlink [as opposed to link]
to fail.  The trick is that if *mail* deletes the symlink we created, we
know [because *our* unlink() returns -1] and from then on, we're golden,
because mail shortly after fopens the tmp file [still as root, and withOUT
checking again!] and copies /usr/spool/mail/me in its entirety to the target.

Exactly right.  You naughty boy, you read the source posted here  :-)

      symlink (av[1], target);
      while (! unlink (target)) symlink (av[1], target);
/* as soon as unlink fails, that means that mail did it.  Reinstall it
   and wait for mail to do the dirty.. */
      symlink (av[1], target);
      sleep (3);
      printf ("Tmp file wound up being %s\n", target);
      unlink (target);
      exit (0);
/* If it never exits, that means you missed, and mail is using some other
   temp file name.  ^C out and clean up after yourself. */

Not bad.  The unlink/symlink loop is essentially the same as mine, except
you used just a single symlink() after mail unlinks yours.  I put it in a
loop for reasons shown below.

The original code is presented here for posterity.  From studying the
recently posted Sun binmail source, I'm not sure why this code worked at all;
it is almost less reliable.  Would the original authors care to explain
their reasoning as I did above, and describe precisely where they're inserting
their crowbar??

Sure I will.

      while (!symlink(TARGET,target)) unlink (target); 
              /* Point that mktemp()'d file to the pot of gold */

Do the same as yours.  Link/unlink until it fails.  Soon, fopen() will
hit our link.

      while (symlink(TARGET,target)) unlink (target);
              /* Probably not necessary, but what the heck */

Link and unlink *while* it fails.  This should just go once (as does yours) 
and then exit, since it will succeed.  But, if it fails, root created the 
mailbox *but* binmail unlinks it again down the line (comments in the source
say to prevent the mail from being read should binmail crash.. the least of
their worries  :-)
I made it a loop on the off chance that it will link in between the unlink and
the fputs that put the mail box in there.  As I put in the program, it
isn't necessary, but why not try?

      kill(bpid,1);  /* Clean up, don't want to lag the system */     

This fails because we don't own the process.

Now, an explanation.  I wrote this program after poking around an Ultrix 4.2
box that a friend runs last year.  He and I had a friendly competition where
I tried to find the holes and he fixed them.  He was usually one step ahead of
me for various reasons, but I vowed I could beat him.  I noticed that the
temp mailboxes were owned by root, and sensed a bug (this was right before the
8lgm scripts about binmail hit news).

I ignored it for a while because I just wasn't interested and I was enjoying
the summer.  This fall, I heard a few rumors about an unreleased 8lgm script
that exploited the same hole I saw on Ultrix.  The source for binmail hit 
bugtraq and I just had to take a peek  :).  Anyways, I threw together a VERY
buggy version that worked off and on on my linux box (I wrote a fake binmail
program that did a mktemp(buf);fp=fopen(buf); fputs("woops.",fp);

I was going to use it on the system, but thought the better of it and then
just mailed a copy to Chris Ellwood.  He fixed it up a bit and it worked about
80% of the time.  I felt that I couldn't sit on it, but I got a sysadmin job
of my own.  I made a hasty post to the local admins list and bugtraq.  I did
it on the spur of the moment because I support full disclosure, but our school
looks down on it sternly.  In fact, I probably shouldn't have posted it as it
made quite a few administrators unhappy here.  I heard a few vague threats of
prosecution (of who, I don't know).  Anyways, I knew the post was pretty lousy
(the variable to check if the length of the target never gets updated), but
I figured you all were smart enough to fix it enough to insure you did or 
didn't have the problem.  Chris and I fixed it ourselves, and have a patched
version that works ~99% of the time.  Unfortunately, my job would probably
be in jeopardy if I did so.  That's why I haven't said anything more about it.

Please do not flame me for not writing a "Grade A" exploit.  I gave you a free
copy of something that took a lot of time to think up and guts to risk a job
over.  Maybe I shouldn't have done so.  Maybe I am not a great admin compared
to the great "Hobbit", but at least I have done my part to point out the 
problems.  And you can't take that away from me no matter how much you pick
apart and discredit my work.

-N8*_



Current thread: