[Mailman-Developers] Cookie security hole in admin interface

John Morton jwm@plain.co.nz
Mon, 14 Jun 1999 12:43:30 +1200 (NZST)


Harald Meland writes:
 > As the extra complexity added by having to save session state on the
 > server side (i.e. have Mailman keep track of session IDs) is rather
 > large, and as Mailman isn't safe from package sniffing anyway (unless
 > you're running things on a SSL server, in which case cookie sniffing
 > shouldn't be of any trouble anyway), I settled for slightly less.
 
True. Though stealing a cookie via packet sniffing will still require
the thief to be on the same IP as the original cookie owner, or it
will require them to fake their IP as well. This definitely makes the
list vulnerable to only an extremely determined attacker.
 
[Note, I'm not a python hacker, so bear with me :-) ]

Do add the domain that this mailman for this list is supposed to be
under (or the browser will send this cookie to every site you connect
to!) and restrict the scripts that it is sent to to /mailman/ at
worst, /mailman/admin and /mailman/admindb separately at best
(allowing separate passwords for those two scripts is a needed feature 
for a proper distinction between list admins and moderators, anyway).

Of course, you could already be doing this and I just missed it :-)

 > I have just commited a fix to CVS, based on these two new
 > SecurityManager functions:
 > 
 >     def MakeCookie(self):
 >         client_ip = os.environ.get('REMOTE_ADDR') or '0.0.0.0'
 >         issued = int(time.time())
 >         expires = issued + mm_cfg.ADMIN_COOKIE_LIFE
 >         secret = self.password

I'd prefer to grab the secret from a file that's refreshed via a
cronjob every 24hrs or so. Generate it from something like a CRC32 of
some /dev/urandom output (on a linux box, anyway) would do the trick.

 >         mac = hash(secret + client_ip + `issued` + `expires`)

Is the hash() function one way? How about:

   import md5
   import base64

   mac = base64.encodestring(md5.new(secret + client_ip + `issued` + `expires`).digest())

The FAQ talks about doing a second round of MD5 hashing to '...avoid
an attack in which additional data is appended to the end of the
cookie and a new hash recalculated by the attacker.', but I don't
really understand why that's necessary.

With any luck, someone will have implemented an HMAC (see RFC 2104, 
http://www.it.kth.se/docs/rfc/rfcs/rfc2104.txt) module for python 
that we could use. 

 >         return [client_ip, issued, expires, mac]
 > 
 >     def CheckCookie(self, cookie):
 >         if type(cookie) <> type([]):
 >             return 0
 >         if len(cookie) <> 4:
 >             return 0
 >         client_ip = os.environ.get('REMOTE_ADDR') or '0.0.0.0'
 >         [for_ip, issued, expires, received_mac] = cookie
 >         if for_ip <> client_ip:
 >             return 0
 >         now = time.time()
 >         if not issued < now < expires:
 >             return 0

Should we check that expires - issued = mm_cfg.ADMIN_COOKIE_LIFE ? 

 >         secret = self.password
 >         mac = hash(secret + client_ip + `issued` + `expires`)

See above.

 >         if mac <> received_mac:
 >             return 0
 >         return 1
 > 
 > Hopefully, this new cookie scheme will suffice -- if anyone do see
 > flaws in it, don't hesitate to get in touch.

The combination of a cryptographic hashing function and a secret
key that's regenerated on a 24hour cycle makes an attack by
constructing a cookie infeasible (at least, within the useful lifetime
of whatever hashing function we're using). 

Remaining sources of attacks are:

Packet sniffing: They could steal the cookie this way. But they'd just 
steal the password, anyway, so it's a moot point. Use SSL if you're
that paranoid. 

Stealing the cookie: Presumably by some method other than packet
sniffing or direct access to your terminal; maybe a broken browser
could be distributing the cookie to every site it meets. The attacker
will need to fake your IP to the web server _and_ get the response
back, which is rather hard. 

Getting access to your terminal: The short expiry time is supposed to
help defeat this problem. The price to pay for the convenience of not
having to type the password in every time.

Perhaps having an option to turn off the use of cookies will keep the
paranoid happy and allow admins to use their servers native
authentication methods. Perhaps modules to interface between mailman
and the various different web servers is a direction someone would
like to go in?

John.