gcc security tips

Here are some flags that may help vulnerable code from being executed.

-D_FORTIFY_SOURCE=2

This should get rid of some buffer overflows that can be analyzed statically and some obvious ones (strcpying input, format string vulnerabilities).

More information can be found here: http://gcc.gnu.org/ml/gcc-patches/2004-09/msg02055.html

-fstack-protector-all

From the man page:

Emit extra code to check for buffer overflows, such as stack smashing attacks.  This is done by adding a guard variable to functions with vulnerable objects.  This includes functions that call alloca, and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits.  If a guard check fails, an error message is printed and the program exits.

(this is enabled by default in recent versions of Ubuntu)

gnu readline – python

This is the very start of our cryptanal program frontend

#!/usr/bin/env python

import readline

"""The shell class is the front end for cryptanal"""

class shell:
  def __init__(self, filename=None):
    print"""
WWW         WW eEeEeEeE LL        CCCCC    OOOO    MMMM    MMMM  eEeEeEeE
 WW         W  EE       LL       Cc      OOO  OOO  MM MM  M  MM  EE
  WW       WW  EeEeE    LL      CC       OO    OO  MM  MMM   MM  EeEeE
  WWw WW  WW   EE       LL       Cc      OOO  OOO  MM        MM  EE
   WWW  WWW    eEeEeEeE LlLlLlL   CCCCC    OOOO    MM        MM  eEeEeEeE

                             TO CRYPTO-SHELL
  (Useful for deciphering what little Susie is writing to little Billy)
"""

    self.filename = filename
    self.crypto = None
    #if self.filename != None:
      #self.crypto = freqcount.subCryptAnal(self.filename)

    #setup the tab completion information here
    self.commands = ["help", "printfreq"]
    readline.set_completer(self.completer)
    readline.parse_and_bind("tab: complete")

  #completer funtion for tab complete
  def completer(self, word, index):
    matches =
    try:
      return matches[index] + " "
    except IndexError:
      pass

  #this is the main event loop
  def mainloop(self):
    while 1:
      command=raw_input('> ').lstrip()
      if command.lower().startswith('help'):
        self.help(command[4:].lstrip())
      else:
        print "Error: command not recognized"

  def help(self, args):
    print "HELP"

if __name__ == '__main__':
  thisshell = shell()
  thisshell.mainloop()

Privilidge Separation in sshd

This was accepted into openssh sometime in 2002.  It helps make openssh exploits more difficult in terms of gaining root.

Do a ps -ef on your system where an underprivilidged user is logged in via ssh.  eg

# ps -ef |grep sshd |grep myuser
root       28694  7865  0 Mar25 ?        00:00:00 sshd: myuser [priv]
myuser     28703 28694  0 Mar25 ?        00:00:00 sshd: myuser@pts/2

While that [priv] may seem disconcerting, it (by itself) shouldn’t be.  There is privilege seperation in sshd  so that if an exploit is found in the child does not result in a system compromise.

http://www.citi.umich.edu/u/provos/ssh/privsep.html

Small Steps

These are some small steps you can take to make it harder for an attacker to figure out what version of some things you have running. These are specific to ubuntu:

apache

Go into /etc/apache2/apache2.conf and change

ServerTokens Full

to

ServerTokens Prod

This will change your info for things like 404s, so instead of listing your exact os, version of apache, etc, it will only say something like ‘Apache Server at progeny.isu.edu Port 80’

postfix

change the smtpd_banner line to something like “smtpd_banner = $myhostname ESMTP $mail_name (Linux)” which will be much harder to figure out than all the crap this normally prints, which, once again, is the exact version of postfix you are running along with the os.

bind

By default, bind also lets people know all this info.  You can change this by changing the version = lines as follows

options {

version “back off, dirt bag!”;

}

Though many of these services can still be fingerprinted without too much effort, not reporting your version info for every service you run is probably a good idea.  So, for example, because of this, you may have a harder time figuring out that this server is running apache2.2 on ubuntu7.04 and postfix2.5… damn it.

fpdns

In an attempt to determint the bind version number remotely, usually something like:

dig @dnsserver.net version.bind txt ch

will give you what you need. However, this is a configuration option that can be turned off. ie, in named.conf they could have set the following

options {

version “back off!”;

}

and, uhh, yeah. that will return “back off!” for our version number, which isn’t very helpful.

Enter fpdns.

From the man page:

fpdns is a program that remotely determines DNS server versions. It does this by sending
a series of borderline DNS queries which are compared against a table of responses and
server versions.

False positives or incorrect versions may be reported when trying to identify a set of
servers residing behind a load-balancing apparatus where the servers are of different
implementations, when a specific implementation behaves like a forwarder, behind a fire‐
wall without statefull inspection or without Application Intelligence.

and it works pretty well from my preliminary tests.

Really global environment variables for ssh

<mopey> how do I export a variable in pvm?  I add it to my .bashrc or .profile but it ignores it.
<mopey> an environment variable
<mopey> Because I get this error:
<mopey> The value of the $PVM_ROOT environment
<mopey> variable on compute-0-1 is invalid (“”).
<mopey> Use the absolute path to the pvm3/ directory.
<mopey> but if I ssh into compute-0-1, echo $PVM_ROOT it is set correctly
<staynalive_> mopey: I don’t know much about PVM
<staynalive_> but I would check to see if it gets set for non-login sessions
<staynalive_> by doing (in one command) “ssh compute-0-1 printenv”
<staynalive_> Yeah, I just tested it and that’s the issue.
<mopey> good call, it’s not being set for some reason, although it’s “being set” in ~/.bashrc
<mopey> where would I set it, if not bashrc?
<staynalive_> Umm
<mopey> my .profile calls bashrc btw, although that shouldn’t really matter since that’s only on interactive logins, right?
<staynalive_> Yeah
<staynalive_> I think I actully changed a ssh flag to carry the environment variables through to the new machine in a ssh session.
<staynalive_> “PermitUserEnvironment yes”
<mopey> oh.  well that’s handy.
<staynalive_> That way if users set something up funky they can carry it to the nodes.
<staynalive_> But the manual warns of some possible security issues…
<mopey> if someone is on my frontend node, it’s only being used on the compute nodes, so it shouldn’t be that big of a deal.
<mopey> since they are basically thin
<mopey> aaah, ssh has env variables all of it’s own…
<mopey> I remember telling you that at one point.  I guess I’m losing my marbles

<mopey> has anyone ever gotten sshrc to succesfully set ssh environment variables?
<mopey> it *should* be straightforward
<mopey> the sshd man page says:  8.  If $HOME/.ssh/rc exists, runs it; else if /etc/ssh/sshrc exists, runs it; otherwise runs xauth.  The “rc” files are
<mopey>  given the X11 authentication protocol and cookie in standard input.
<mopey> is this not run (when I do ‘ssh compute-0-0 env’) because it is too late in the process?
<mopey> because my $HOME/.ssh/environment _is_ run, and I can set them that way.  Except that I want to do it for all users and that seems to be a lame solution.
<mopey> It seems like the command should be executed *after* the rc files are read (it’s step 9)
<mopey> So I wonder why the hell it’s being ignored…
<mopey> staynalive, you said you use “PermitUserEnvironment yes”.  So do you just set a $HOME/.ssh/environment for each user?

<mopey> haha
<mopey> for those who care, pam overrides my ssh variables for the most part
<mopey> so you can define variables in /etc/security/pam_env.conf
<mopey> god, that took forever to figure out why my /etc/environment variables weren’t getting set over ssh
* Vog-work has quit (“ChatZilla 0.9.79 [Firefox 2.0.0.10/2007111504]”)
<twinprism> thanks for sharing, mopey, I care…
<mopey> weird.
<mopey> :)
<_sera> I don’y
<_sera> sheesh… don’t
<mopey> Normally it probably doesn’t matter I’m sure.  Like if you get a bash shell and can actually execute profile/bashrc
<mopey> But if you have a crippled pvm shell or something, it’s way important
<mopey> plus I think pam_env is how PATHs and junk get set on login – at least on ubuntu
<mopey> *gdm/kdm/xdm login

chkrootkit

chkrootkit operates sort of like a virus scanners for windows in a way – in that it looks for infected files from signatures.

From the man page:

chkrootkit examines certain elements of the target system and determines whether they have been tampered with. Some tools which chkrootkit applies while analyzing binaries and log file  can be found  at  /usr/lib/chkrootkit.

I installed using apt-get.

By default, it logs to a file. I like to check my logs over email every morning, so I changed the cron job to reflect this. I added the MAILTO: root line, and the /usr/bin/chkrootkit at the end (the standard output is what gets mailed).  The following entry is /etc/cron.daily/chkrootkit.  So it gets logged and mailed.

#!/bin/sh -e

CHKROOTKIT=/usr/sbin/chkrootkit
CF=/etc/chkrootkit.conf
LOG_DIR=/var/cache/chkrootkit
MAILTO=root

if [ ! -x $CHKROOTKIT ]; then
exit 0
fi

if [ -f $CF ]; then
. $CF
fi

if [ "$RUN_DAILY" = "true" ]; then
  if [ "$DIFF_MODE" = "true" ]; then
    $CHKROOTKIT $RUN_DAILY_OPTS > $LOG_DIR/log.new 2>&1
    if [ ! -f $LOG_DIR/log.old ] \
      || ! diff -q $LOG_DIR/log.old $LOG_DIR/log.new > /dev/null 2>&1; then
      cat $LOG_DIR/log.new
    fi
    mv $LOG_DIR/log.new $LOG_DIR/log.old
  else
    $CHKROOTKIT $RUN_DAILY_OPTS
  fi
fi
/usr/sbin/chkrootkit

chkrootkit seems like it has quite a bit of promise.  I use chkrootkit with tripwire, selinux, iptables, fail2ban, and good service configuration for a functional system that is still fairly secure.