Linux The Secureable Operating System Bri Hatch LinuxFest Northwest April 26, 2003 Bellingham, WA Copyright 2003, Bri Hatch ______________________________________________________ AKA "Every Linux security hook in 60 minutes or less. ... or your money back." ______________________________________________________ What's Linux? Linux GNU/Linux? Or perhaps Apache/GNU/Linux OpenSSL/Apache/GNU/Linux OpenSSH/OpenSSL/Apache/GNU/Linux ______________________________________________________ Goal of this talk Get you interested in all the security features Linux offers This will be a lightning fast presentation -- read these slides later online Show some programming pitfalls that you should and can avoid Marvel in the simplicity and power of things like file permissions, and their applications throughout the system Let you realize that Linux is capable of such good security that you could give out your root password and not be worried. www.hackinglinuxexposed.com webserver current IP address: 209.207.4.34 username: root password: $4X.>ou$`:F%B[5tI3#G ______________________________________________________ What I won't discuss User security issue $IFS hacks, $PATH problems, password/account sharing, authentication methods (shadow, one time passwords, LDAP, SSH keys, PAM, kerberos, biometrics, two factor authentication, etc) Common user programming mistakes strcpy, strcat, gets, sprintf, vsprintf, trusting user input, using csh User-space security issues buffer overflows, format string attacks, viruses, trojans, TCP Wrappers, restricted shells, LD_PRELOAD Administration/configuration issues Running unnecessary services, poor file permissions, Audit / Log analysis / Scanning tools Tripwire, AIDE, Tiger, SATAN's offspring, NSAT, swatch, Nessus, etc. ______________________________________________________ What I won't discuss (cont) Physical security Keystroke logger dongles, tempest monitor attacks Boot security lilo/grub/bios passwords, sulogin User-space security tools StackGuard, FormatGuard, tmpwatcher Bugs in specific software BIND, Sendmail, OpenSSL timing attacks, Apache Denial of Service attacks, BIND, Mutt imap vulnerability, Sendmail, BIND... Known bad ideas rsh / rlogin / telnet, xhost+, logging in as root Anything I've inadvertantly omitted ______________________________________________________ Disclaimer In order to keep the presentation focused, I will frequently take shortcuts: Not all #includes are shown in C code Return values frequently not checked in examples Variables seldom defined Spacing in output often condensed Each page is only internally consistant Uid 1001 may be 'reegen' on one page, and 'taxee' on the next. Output irrelevant to the discussion at hand frequently removed Bye-bye parent directory listings Legitimate bugs occasionally inserted to test the audience That's my story, and I'm sticking to it. ______________________________________________________ Users and Groups Every Linux account has Username, e.g. 'bob' Primary group, e.g. 'users' Supplimentary groups (e.g. "web", "cgi") Real name Authentication information (/ec/shadow entry, etc) Shell Home directory Dotfiles (~/.profile, ~/.vimrc, ~/.mozilla) $ grep ^bree: /etc/passwd bree:x:1000:1000:Bree Urban:/home/bree:/usr/bin/ksh ______________________________________________________ Processes Every Linux process has Userid (real/effective/etc) Groupid (real/effective/etc) Supplimental groupids Process group ID Environment variables (TERM=vt100, EDITOR=vi) Command line args Virtual memory space Open file descriptors Signal handlers Limits ______________________________________________________ Linux Threads Linux threads are like multiple processes that share the same virtual memory. They even have different PIDs, and appear multiple times in 'ps' and 'top' output. PID USER SIZE RSS SHARE STAT %CPU %MEM TIME COMMAND 7185 bri 1488 1488 1412 S 0.0 0.5 0:00 stunnel 7186 bri 1488 1488 1412 S 0.0 0.5 0:00 stunnel 7187 bri 1488 1488 1412 S 0.0 0.5 0:00 stunnel 7188 bri 1488 1488 1412 S 0.0 0.5 0:00 stunnel 7189 bri 1488 1488 1412 S 0.0 0.5 0:00 stunnel Threads need to be ultra careful to not affect each other in undesired ways. Locking mechanisms are used to cooperate when accessing data that may be in use by other threads. ______________________________________________________ What does Linux know? The Linux kernel only 'knows' about processes, not users. Processes are launched by other processes. Processes inherit the same 'settings' (uid/gid/env/etc) as the parent, generally. Processes can execute new processes with different 'settings', such as via setuid/setgid bits, explicit environment, etc Processes inherit file descriptors (open files) by default. ______________________________________________________ Processes Interaction Processes cannot interact by default. However they can communicate using Pipes, shared memory, semaphores, and other IPC (interprocess communication) mechanisms $ ls -l | grep "Apr 19" | awk '{print $9}' FIFOs, sockets, network sockets, files, etc $ mknod /tmp/named_pipe p $ ls -l /tmp/named_pipe prw------- 1 reegen reegen 0 Apr 19 16:03 /tmp/named_pipe $ echo "Hello World!" >> /tmp/named_pipe & $ cat /tmp/named_pipe Hello World! Signals (HUP, TERM, USR1, KILL, etc) $ killall -HUP syslogd ______________________________________________________ Security implications The fact that processes can't communicate or interact without specifically enabling it leads to security as a pleasant side effect! Processes can't affect memory of other processes Limits memory-leak related crashes Your web browser can't access your email program and spam your friends When restrictive file permissions are used, one user/program can't access another's data inappropriately You can create files and directories readable only to you, or certain groups, with fairly fine-tailored control. A malicious user can't directly access the database files, he must go through a properly authenticated connection ______________________________________________________ Environment example $ /usr/bin/printenv EDITOR vi $ cat envexample #!/bin/sh echo "EDITOR is $EDITOR" export EDITOR=emacs echo "EDITOR is $EDITOR" $ ./envexample EDITOR is vi EDITOR is emacs $ /usr/bin/printenv EDITOR vi ______________________________________________________ Users and Groups User and group membership is set at login time, or by programs like 'newgrp'. $ id uid=1000(bree) gid=1000(bree) groups=1000(bree),50(sea),1001(web) $ grep ^bree /etc/passwd bree:x:1000:1000:Bree Urban:/home/bree:/usr/bin/ksh $ grep bree /etc/group seattle:x:50:bri,reegen,bree,sabrina bree:x:1000: web:x:1001:bri,bree ______________________________________________________ File ownership Each file is 'owned' by a user and a group: $ touch somefile $ ls -l somefile; ls -ln somefile -rw-rw-rw- 1 reegen seattle 0 Apr 19 2000 somefile -rw-rw-rw- 1 1001 500 0 Apr 19 2000 somefile | | | \- group ownership | \- user ownership $ id uid=1001(reegen) gid=500(seattle) groups=500(seattle) The kernel doesn't ever see symbolic names, just the uid/gid. User to uid mapping is in /etc/passwd. Group to gid mapping is in /etc/group. ______________________________________________________ Changing file ownership You can change group who owns a file if You are root, or You own the file, and You want to change the group to a group of which you are a member $ id uid=1001(reegen) gid=500(seattle) groups=500(seattle),5002(web) $ ls -l somefile -rw-rw-rw- 1 reegen seattle 0 Apr 19 2000 somefile $ chgrp web somefile; ls -l somefile -rw-rw-rw- 1 reegen web 0 Apr 19 2000 somefile $ chgrp cgi somefile; ls -l somefile chgrp: Operation not permitted -rw-rw-rw- 1 reegen web 0 Apr 19 2000 somefile ______________________________________________________ Changing file ownership (cont) You can change user who owns a file if You are root. You are on a very dumb Unix-like OS that allows file giveaways by default[1]. # id uid=0(root) gid=0(root) groups=0(root) # ls -l somefile -rw-rw-rw- 1 reegen web 0 Apr 19 2000 somefile $ chown bree:movies somefile; ls -l somefile -rw-rw-rw- 1 bree movies 0 Apr 19 2000 somefile [1] Can you say IRIX? ______________________________________________________ File Permissions The Linux kernel uses file permissions as the most basic access control mechanism. The file permissions are represented by the first field you see in 'ls -l' output: $ ls -la drwxr-xr-x 2 reegen reegen 4096 Apr 19 2000 . drwxr-xr-x 2 root root 4096 Feb 21 1973 .. -rw------- 1 reegen reegen 9663 Oct 8 16:03 .profile -rw-r--r-- 1 reegen reegen 9663 Oct 8 16:03 .plan drwxrwxrwx 1 reegen reegen 4096 Jan 22 10:05 dropbox drwxr-x--- 1 reegen friends 8925 Jan 12 11:17 music drwx------ 1 reegen reegen 4096 Dec 6 8:50 private ______________________________________________________ File Permissions Breakdown The first part of each line: drwxrwxrwx 1 reegen reegen 4096 Jan 22 10:05 dropbox can be broken up as follows: d rwx rwx rwx | | | | | | | \-> "Other" permissions | | | | | \-> "Group" permissions | | | \-> "User" permissions | \-> Type of file ______________________________________________________ File Permissions Breakdown Thus, we have drwxr-x--- 1 reegen friends 8925 Jan 12 11:17 music d rwx r-x --- | | | | | | | \-> "Other" perms (everyone else is denied) | | | | | \-> "Group" perms ('friends' can read/execute) | | | \-> "User" perms ('reegen' can read/write/execute) | \-> Type of file (directory) ______________________________________________________ File Permission bits for files: Read File can be opened in read mode Write File can be opened in write or append mode Execute File can be run as a compiled program (if it's a recognized binary) as a shell/perl/etc script (if it has a valid shebang line.) #!/bin/sh #!/usr/bin/perl -w -p #!/usr/bin/python ______________________________________________________ File permission bits for directories: Read Filenames in the directory names can be listed Write If you have execute permission to the directory, then files in this directory can be created deleted renamed Execute The ability to cd into the directory access the files themselves in the directory ______________________________________________________ Changing permissions with chmod Only the file/dir owner (or root) can change permissions! $ mkdir music; ls -ld music drwx------ 1 reegen reegen 4096 Jan 12 11:17 music $ chmod u=rwx,g=rx,o= music; ls -ld music drwxr-x--- 1 reegen reegen 4096 Jan 12 11:17 music $ chgrp friends music; ls -ld music drwxr-x--- 1 reegen music 4096 Jan 12 11:17 music Or, using octal notation user group other rwx r-x --- => 421 421 421 == 7 5 0 $ chmod 750 music ______________________________________________________ Umask setting (symbolic) The umask setting strips out bits from the default permissions for new files Files are created rw-rw-rw by default. Directories are created rwxrwxrwx by default. $ umask -S u=rwx,g=rx,o=rx $ touch file1 ; mkdir dir1 ; ls -ld file1 dir1 drwxr-xr-x 2 reegen reegen 512 Feb 5 11:16 dir1 -rw-r--r-- 1 reegen reegen 0 Feb 5 11:16 file1 $ umask u=rwx,g=rx,o= $ umask -S u=rwx,g=rx,o= $ touch file2 ; mkdir dir2 ; chgrp friends * ; ls -ld file2 dir2 drwxr-x--- 2 reegen friends 512 Feb 5 11:17 dir2 -rw-r----- 1 reegen friends 0 Feb 5 11:17 file2 ______________________________________________________ Umask setting (cont) Umask is slightly more confusing in the octal world $ umask -S u=rwx,g=rx,o= $ umask 027 user group other rwx rwx rwx if umask is 027 000 010 111 then ~umask is 111 101 000 directory default perms 111 111 111 == rwx rwx rwx bitwise AND with ~umask above 111 101 000 == rwx r-x --- ----------------- yields 111 101 000 == rwx r-x --- ______________________________________________________ Common umask settings umask 002 / umask u=rwx,g=rwx,o=rx Allow read/execute for everyone, only allow write by user and group umask 022 / umask u=rwx,g=rx,o=rx Allow read/execute for everyone, only allow write by user umask 027 / umask u=rwx,g=rx,o= Allow read/execute for user and group, only allow write by user umask 077 / umask u=rwx,g=,o= Allow read/write/execute for user, deny everyone else. umask 777 / umask u=,g=,o= You are root, or You are more paranoid than I, or You don't even trust yourself. ______________________________________________________ Special permission bits There are three other common permission bits: $ ls -la /usr/bin/write -rwxr-sr-x 1 root tty 7540 Jul 4 2002 write - --s --s --t | | | | | | | \-> sticky bit (t or T) | | | | | \-> setgid bit (s or S) - "set group id" | | | \-> setuid bit (s or S) - "set user id" | \-> Type of file For example -rwxr-sr-x 1 root tty 7540 Jul 4 2002 write -rwxr-sr-x 1 root utmp 173820 Apr 7 2002 screen -rwxr-sr-x 1 root shadow 17362 Apr 7 2002 xscreensaver -rwsr-xr-x 1 root root 24680 Apr 7 2002 passwd -rwsr-xr-- 1 root dip 230604 Dec 10 2001 pppd* ______________________________________________________ The setgid bit on a directory When a directory has the setgid bit set any files created in that directory will have the same group membership as the directory: any directories created in that directory will have the setgid bit set $ id uid=1010(bri) gid=1010(bri) groups=1010(bri),2030(friends) $ umask u=rwx,g=rx,o= # umask 027 $ mkdir dir $ chgrp friends dir $ cd dir $ ls -ld . drwxr-x--- 2 bri friends 4096 Apr 19 4:43 . $ touch file; ls -l file -rw-r----- 1 bri bri 0 Apr 19 4:43 file ______________________________________________________ Setgid bit on directories (cont) $ chmod g+s . ; ; ls -la . drwxr-s--- 2 bri friends 4096 Apr 19 4:43 . -rw-r----- 1 bri bri 0 Apr 19 4:43 file # touch newfile ; ls -la drwxr-s--- 2 bri friends 4096 Apr 19 4:43 . -rw-r----- 1 bri bri 0 Apr 19 4:43 file -rw-r----- 1 bri friends 0 Apr 19 4:43 newfile ______________________________________________________ Sticky bit on directories bri$ umask 027 bri$ mkdir /data/pictures ; cd /data/pictures ; bri$ cp /mnt/camera/img* . ; ls -la drwxrws--- 2 bri family 4096 Apr 19 4:43 . -rw-r----- 1 bri family 527244 Apr 19 4:43 img102883.jpg -rw-r----- 1 bri family 462312 Apr 19 4:43 img102884.jpg bree$ cd /data/pictures bree$ cp ~/pictures/scan* . bree$ ls -l -rw-r----- 1 bri family 527244 Apr 19 4:43 img102883.jpg -rw-r----- 1 bri family 462312 Apr 19 4:43 img102884.jpg -rw-rw-r-- 1 bree family 1364772 Sep 17 12:20 scan-01.tiff -rw-rw-r-- 1 bree family 2001827 Sep 17 12:20 scan-02.tiff reegen$ cd /data/pictures reegen$ rm *2* reegen$ ls -l -rw-rw-r-- 1 bree family 1364772 Sep 17 12:20 scan-01.tiff ______________________________________________________ Sticky bit on directories (cont) # Bri and Bree go and copy in their files again, and then... bri$ chmod +t . bri$ ls -la drwxrws--T 2 bri family 4096 Apr 19 4:43 . -rw-r----- 1 bri family 527244 Apr 19 4:43 img102883.jpg -rw-r----- 1 bri family 462312 Apr 19 4:43 img102884.jpg -rw-rw-r-- 1 bree family 1364772 Sep 17 12:20 scan-01.tiff -rw-rw-rw- 1 bree family 2001827 Sep 17 12:20 scan-02.tiff reegen$ rm * rm: cannot unlink `img102883.jpg': Operation not permitted rm: cannot unlink `img102884.jpg': Operation not permitted rm: cannot unlink `scan-01.tiff': Operation not permitted rm: cannot unlink `scan-02.tiff': Operation not permitted reegen$ ls -l -rw-r----- 1 bri family 527244 Apr 19 4:43 img102883.jpg -rw-r----- 1 bri family 462312 Apr 19 4:43 img102884.jpg -rw-rw-r-- 1 bree family 1364772 Sep 17 12:20 scan-01.tiff -rw-rw-rw- 1 bree family 2001827 Sep 17 12:20 scan-02.tiff ______________________________________________________ File permission bits synopsis To explicitly create a permissions list, use the following table: 04000 or u=s -- setuid bit 02000 or g=s -- setgid bit 01000 or =t -- sticky bit 00400 or u=r -- user read bit 00200 or u=w -- user write bit 00100 or u=x -- user exec bit 00040 or g=r -- group read bit 00020 or g=w -- group write bit 00010 or g=x -- group exec bit 00004 or o=r -- other read bit 00002 or o=w -- other write bit 00001 or o=x -- other exec bit ______________________________________________________ The /proc interface The configuration of the running Linux kernel can be examined through the /proc filesystem. /proc entries are simply 'hooks' into the kernel, being presented as a filesystem. Previously, ugly system calls or other hacks were necessary to see the running configuration. $ ls -la /proc/version -r--r--r-- 1 root root 0 Apr 23 09:44 /proc/version $ cat /proc/version Linux version 2.2.21 (bri@ifokr.org) #1 Fri Feb 28 11:45:28 EST 2003 $ ls -la /proc/ide/ide1/hdc/model -r--r--r-- 1 root root 0 Apr 23 09:46 ide/ide1/hdc/model $ cat /proc/ide/ide1/hdc/model TOSHIBA DVD-ROM SD-C2302 ______________________________________________________ The /proc interface (cont) You can change configurable characteristics of the kernel at runtime via /proc entries! $ ls -l /proc/sys/fs/file-max -rw-r--r-- 1 root root 0 Feb 23 09:46 file-max $ cat /proc/sys/fs/file-max 1024 $ echo 4096 > /proc/sys/fs/file-max $ cat /proc/sys/fs/file-max 4096 $ ls -l /proc/sys/net/ipv4/tcp_syncookies -rw-r--r-- 1 root root 0 Feb 23 09:46 tcp_syncookies $ cat /proc/sys/net/ipv4/tcp_syncookies 0 $ echo 1 > /proc/sys/net/ipv4/tcp_syncookies $ cat /proc/sys/net/ipv4/tcp_syncookies 1 See 'man proc' and 'man sysctl' for more examples ______________________________________________________ Device Access Devices are accessed via files in /dev. Drive Partitions brw-rw---- 1 root disk 3, 0 Jul 5 2000 /dev/hda brw-rw---- 1 root disk 3, 1 Jul 5 2000 /dev/hda1 brw-rw---- 1 root disk 3, 2 Jul 5 2000 /dev/hda2 External Devices / Hardware crw------- 1 root root 10, 134 Dec 20 16:58 apm_bios crw-rw---- 1 root audio 14, 4 Jul 5 2000 audio lrwxrwxrwx 1 root root 3 Nov 20 13:46 cdrom -> hdd brw-r----- 1 root disk 22, 64 Jul 5 2000 /dev/hdd crw-rw---- 1 root dialout 4, 67 Mar 27 16:43 /dev/modem TTYs crw------- 1 bri tty 4, 0 Apr 18 12:01 /dev/tty0 crw--w---- 1 bree tty 4, 1 Apr 18 14:00 /dev/tty1 crw------- 1 root root 4, 4 Apr 10 17:49 /dev/tty4 crw------- 1 reegen tty 136, 2 Mar 5 8:13 /dev/pts/2 ______________________________________________________ Device Access (cont) Connectivity to daemons, etc srw-rw-rw- 1 root root 0 Mar 6 15:41 /dev/log prw------- 1 root root 0 Mar 6 15:41 initctl srwxrwxrwx 1 root root 0 Apr 10 17:50 /dev/gpmctl Connectivity to kernel crw-rw-rw- 1 root root 0 Mar 6 15:41 random cr--r--r-- 1 root root 0 Mar 6 15:41 urandom ______________________________________________________ Device access (cont) Device access is controlled by standard Linux file permissions. Modem access requires group 'dialout' $ ls -la /dev/modem crw-rw---- 1 root dialout 4, 67 Mar 27 16:43 /dev/modem $ ls -la /usr/bin/minicom /usr/bin/cu -rwxr-xr-x 1 root uucp 169132 Nov 20 2001 minicom -rwxr-xr-x 1 root uucp 371742 Dec 12 2000 cu $ grep dialout /etc/group dialout:x:20:bri ______________________________________________________ Device access (cont) Device access is controlled by standard Linux file permissions. Ability to write to terminals requires group 'tty' $ ls -la /dev/tty1 crw--w---- 1 bree tty 4, 1 Apr 18 14:00 /dev/tty1 $ grep tty /etc/group tty:x:5: $ ls -la /usr/bin/write -rwxr-sr-x 1 root tty 7540 Jul 4 2002 write ______________________________________________________ Dangers of direct device access Raw access to devices Bypasses the kernel's "helpful" presentation of the device Requires that you know what the hell you're doing Great potential to destroy Can be used to circumvent security! ______________________________________________________ Direct device access dangers (cont) Raw access to hard drive Read access (group 'disk' for example) Good for backups with 'dump' Can let you read /etc/shadow (bad) Can let you scouer swap space for passwords, etc Write access (root only) Can let you modify /etc/shadow Can let you repartition hard drive dd if=/dev/zero of=/dev/hda bs=10240 ______________________________________________________ Direct device access dangers (cont) Raw access to /dev/kmem Read any data on system Password stored in process memory (IMAP cache pw, etc) View network socket data Cleartext streams from crypto filesystems, GPG files in memory Write access kill any process change state/variables/etc of processes change uid of any process to root ______________________________________________________ Root privileges The root user (uid 0) is the only user that can perform certain actions: Read/write/chown/chmod any file, regardless of permissions/owner Bind low (<1024) network ports To provide inbound services To create outbound services that should be 'trusted'. Access raw network devices to create custom packets (nmap), create ICMP packets (ping), change interface parameters (IP address) Become another user/group Use advanced filesystem attributes Reboot machine Increase process priority (renice --20 -p PID) Send signals to any process ______________________________________________________ Root privs by file permissions Many root-only actions are still only controlled by file permissions, not specific kernel restrictions Replace or install system software Edit software/daemon configuration files Access /dev files Modify kernel configuration via /proc entries ______________________________________________________ Extra privileges required The problem is what to do when a program running as a normal user needs to have extra priviliges: Users should be able to 'ping' Requires ability to make and receive ICMP packets Users' screen saver should be able to verify your password before letting you in Requires read access to /etc/shadow Users should be able to write to other user's terminals Requires group 'tty' Graphical terminals need to be able to write to /var/log/wtmp Requires group 'utmp' ______________________________________________________ Kinds of ids Real uid Effective uid Saved uid Filesystem uid Real gid Effective gid Saved gid Filesystem gid Supplimental gids ______________________________________________________ Write example Many times you need special priviliges to open files or perform other actions that are not available to your existing uid/gid. $ ls -la /dev/tty1 crw--w---- 1 bree tty 4, 1 Apr 18 14:00 /dev/tty1 $ id uid=1010(bri) gid=1010(bri) groups=1010(bri) $ ls -la /usr/bin/write -rwxr-sr-x 1 root tty 7540 Jul 4 2002 write /usr/bin/write is run with group 'tty' because of the 's' bit in the group field. ______________________________________________________ The setuid bit on programs When a program with the setuid bit set is run, it runs with an 'effective' uid of the owner of the file # cp /usr/bin/id ./setxid # ls -l setxid -rwxr-xr-x 1 root root 13052 Apr 19 4:43 setxid # chmod u+s setxid # ls -l setxid -rwsr-xr-x 1 root root 13052 Apr 19 4:43 setxid $ /usr/bin/id uid=1010(bri) gid=1010(bri) groups=1010(bri) $ ./setxid uid=1010(bri) gid=1010(bri) euid=0(root) groups=1010(bri) ______________________________________________________ The setgid bit on programs When a program with the setgid bit set is run, it runs with an 'effective' gid of the group of the file # cp /usr/bin/id ./setxid # ls -l setxid -rwxr-xr-x 1 root root 13052 Apr 19 4:43 setxid # chgrp web setxid # chmod u+s setxid # ls -l setxid -rwxr-sr-x 1 root web 13052 Apr 19 4:43 setxid $ /usr/bin/id uid=1010(bri) gid=1010(bri) groups=1010(bri) $ ./setxid uid=1010(bri) gid=1010(bri) egid=0(web) groups=1010(bri) ______________________________________________________ SetXid bits on scripts SetXid scripts are a *bad* idea. Ability to add additional 'commandline' args, such as notlinux$ head -1 thescript #/bin/sh notlinux$ ls -la thescript -rwsr-xr-x 1 root web 5012 Apr 19 4:43 thescript notlinux$ thescript -i $ id uid=1010(bri) gid=1010(bri) euid=0(root) groups=1010(bri) SetXid scripts are run without setXid perms on Linux. Can run scripts with setXid perms using compiled wrapper programs Can run Perl scripts with setXid perms using suidperl ______________________________________________________ SetXid problems Any program that runs with additional privileges is a potential security problem. Can read/write files unaccessable by the invoking user What if 'chage' had a bug, and allows you to see all /etc/shadow entries? What if 'eterm' had a bug and allows you to edit /var/log/wtmp? Can access capabilities reserved for root iff suid root. What if 'ping' had a bug and allows you ping flood hosts and/or broadcast? What if 'procmail' had a bug and allowed you to overwrite any file? What if 'pppd' had a bug and allows you invoke a shell via 'pty' argument? ______________________________________________________ When are permissions checked? The kernel only performs access control checks for an object (file descriptor) when it is initially accessed or created. Root-only system calls (setuid, etc) are checked each time they are attempted. Programs can open a privileged resource with enhanced privs, drop those privs, and still access the resource. Side effects: Allows better performance restrictions checked once, not at every read/write, for example Changing permissions of a file after a bad guys has it open does not prevent his further access to it! ______________________________________________________ Dropping privileges How to avoid problems when writing programs that are setXid? Simple: Gain access to whatever protected resources you need as early as possible. Open protected files, create a raw socket, bind a low port, etc Drop your enhanced privs using the set*id system calls Do whatever needs doing ______________________________________________________ Set*id system calls SetXid programs are running with effective uid/gid, but original uid/gid are still known to the program. Thus a setXid program can easily get back to the original uid/gid settings. Useful system calls include: setuid( uid ); setgid( gid ); Set real id (and effective/saved/fs iff root) setreuid( ruid, euid ); setregid( rgid, egid ); set real and effective ids (can use -1 to indicate no change) setresuid( ruid, euid, suid ); setresgid( rgid, egid, sgid ); set real, effective, and saved ids (can use -1 to indicate no change) ______________________________________________________ Setgid example Compile a program like the following main() { int fd; /* get access to privileged resource */ fd = open( "/etc/shadow", O_RDONLY ); /* become invoking user again */ if ( setgid( getgid() ) < 0 ) { printf(stderr, "Can't get back to original gid - bailing out!\n"); exit(1); } /* do something with fd now */ } ______________________________________________________ Setgid example (cont) Compile and set proper permissions # gcc -o shadow-reader shadow-reader.c # chgrp shadow shadow-reader # chmod g+s shadow-reader # ls -l shadow-reader -rwxr-sr-x 1 root shadow 17632 Sep 17 10:40 shadow-reader Now, whenever someone runs 'shadow-reader' it will be able to access /etc/shadow: # ls -l /etc/shadow -rw-r----- 1 root shadow 827 Apr 25 10:40 /etc/shadow ______________________________________________________ Complex example Using the saved uid to get back to root later: setresuid( getuid(), getuid(), 0 ); /* become user, with saved uid */ /* do something as user */ system("/some/program"); fd = open("/some/file", O_CREAT | O_TRUNC | O_EXCL, 0666); setresuid( 0, 0, 0 ); /* get back our full root privs */ /* do something as root */ system("/usr/bin/id"); This method is prone to mistakes when you try to manipulate id's back and forth frequently. (And using system is a big big no-no.) ______________________________________________________ Fork method Fork and run setgid/setuid to be sure you don't have any extra privs. pid = fork(); if ( pid > 0 ) { /* parent, let's just wait */ waitpid( pid, &status, 0); } elsif ( pid == 0 ) { /* child - become the actual user */ setgid( gid ); setuid( uid ); /* setgroups() or initgroups() may be appropriate here */ if ( gid != getgid() or uid != getuid() ) { diehorribly(); } /* do stuff as the unprivileged user here*/ execl("/usr/bin/vi", "vi", "/tmp/somefile", NULL) } else { diehorribly(""); } ______________________________________________________ Uid switching in Perl Yes, you can even use setuid/setgid in Perl. $uid = 10; $gid = 100; # setuid($uid); $< = $uid; $UID = $uid; $REAL_USER_ID = $uid; # seteuid($uid) $> = $uid; $EUID = $uid; $EFFECTIVE_USER_ID = $uid; # setgid($gid) $( = $gid; $GID = $gid; $REAL_GROUP_ID = $gid; # setegid($gid) $) = $gid; $EGID = $gid; $EFFECTIVE_GROUP_ID = $gid; Make sure you check that $UID == $uid (etc) after setting it! ______________________________________________________ Filesystem uids / gids Each process has a 'filesystem' uid and gid. All file access restrictions are actually checked against fsuid / fsgid. Files are created with fsuid / fsgid ownership. fsuid and fsgid are always changed to whatever euid and egid are set to whenever euid/egid are changed. The setfsuid and setfsgid calls can be used to change these values. New fsuid must be same as uid, euid, or suid. New fsgid must be same as gid, egid, or sgid. Very very few programs use these system calls. NFS server Some Spagetti code I wrote very very long ago.... ______________________________________________________ Extended file attributes Extended file attributes can provide greater file access restrictions. Only available on ext2 / ext3 filesystems. Attributes cannot be set or read on NFS mounted filesystems, but are in force. Can only be changed by root. Can be changed back by cracker who has gotten root. Includes such attributes as a - Append only d - Not dumpable i - Immutable S - Synchronous writes ______________________________________________________ Chattr in action # lsattr /var/log/messages -------------- /var/log/messages # chattr +a /var/log/messages # lsattr /var/log/messages ---a---------- /var/log/messages # echo "Please truncate file" > /var/log/messages Operation not permitted # echo "Please append to file" >> /var/log/messages (no error) # chattr -a /var/log/messages # chattr +i /var/log/messages # lsattr /var/log/messages ---i---------- /var/log/messages # echo "Please append to file" >> /var/log/messages Permission denied ______________________________________________________ Filesystem mount options There are many mount options, some security related. nosuid - Do not allow any setXid programs to be run on this filesystem. noexec - Do not allow any programs at all to be run on this filesystem. nodev - Don't honor device files on this filesystem ro - mount filesystem read-only rw - mount filesystem read/write encryption - for mounting encrypted filesystems noatime - Don't update atime on files noauto - do not mount this filesystem when 'mount -a' called sync - Use synchronous I/O mand - Mandatory locking (more later) user - allow a non-root user to mount/unmount the filesystem. (implies noexec,nosuid,nodev) users - same as user, but any user can umount. ______________________________________________________ /etc/fstab Mount options are stored in /etc/fstab /dev/hda1 / ext2 rw,sync 1 1 /dev/hda2 /boot ext3 ro 1 2 /dev/hda3 /usr ext3 ro,nodev 1 2 /dev/hda4 /var ext3 noexec,nosuid 1 2 /dev/hda6 /home reiserfs noexec,nosuid,noatime 1 2 /dev/hda7 /www reiserfs noexec,nosuid,noatime 1 2 /dev/fd0 /mnt/floppy vfat noauto,user 0 0 /dev/hdc /mnt/cdrom iso9660 noauto,user,ro 0 0 ______________________________________________________ What's wrong with this code? /* Running as uid==someuser, euid==root */ /* Be sure target file is owned by target user */ if ( stat(FILENAME, &stats) == -1 || /* file doesn't exist */ stats.st_uid == getuid() )) { /* file owned by user */ fd = open(FILENAME, O_CREAT|O_TRUNC, 0666); /* Create file */ write(fd, buf, srtlen(buf) ); /* write out buffer */ close(fd); /* Change owner of file to the target user, */ /* in case it was newly created */ chown(FILENAME, getuid(), getgid()); } else { fprintf(stderr, "Sorry, filename exists and isn't owned by target\n"); } ______________________________________________________ Race conditions Linux is a multitasking operating system. Other processes will get the CPU while your program is still executing. The validity of any checks you do is suspect, because the state may change. Suggestions Never do checks and actions separately when atomic functions are available. Operate on open files, rather than file names, whenever possible. If you need to do too many checks, there's probably a better way. If you are trying to do stuff as root for other users, better to fork/setuid and do the actions as the other user. ______________________________________________________ Symlink attacks Two main symlink attacks: Pointing to system files anurup$ cd ~ anurup$ ls -l file lrwxrwxrwx 1 anurup anurup 11 Dec 0610:19 file -> /etc/shadow Dangling symlinks anurup$ cd /tmp anurup$ ls -l file lrwxrwxrwx 1 anurup anurup 11 Dec 0610:19 file -> /root/.shosts anurup$ ls -l /root/.shosts ls: No such file or directory ______________________________________________________ Stat() calls There are four stat-like calls The stat() system call will provide the statistics about the target file - it will follow symlinks. The lstat() system call will provide the statistics about the link itself, if it is a link, otherwise same as stat. The fstat() system call operates on an already-opened file descriptor. Immune to race conditions - the file is already open Need to have already opened the file in a safe manner. The access() system call lets you know if a potential action (read/write) would succeed on a particular file. ______________________________________________________ Use fstat() Use fstat whenever possible. Only use stat(), lstat(), or access() to provide proactive checks. "Hey, user, I think this won't work, would you like me to try anyway?" Just try the action safely. If it works, great. If not, deal with the error. ______________________________________________________ Using open Open will follow symlinks and dangling symlinks. It's tempting to do stat calls first to be sure you're not tricked, but that's still a race condition. If you're sure the file should not exist before you create it, use the O_EXCL flag to open. fd = open( FILENAME, O_CREAT | O_RDWR | O_EXCL, 0666 ); or for Perl folks use Fcntl; sysopen( HANDLE, $filename, O_CREAT | O_RDWR | O_EXCL ) or die; # mode defaults to 0666 # perldoc perlopentut for more wonders ______________________________________________________ Opening existing files If you need to open a file that is supposed to already exist open file (without O_CREATE) fstat file be sure user/group is what you expect fstat is available in Perl via "use POSIX (:fstat);" ______________________________________________________ Proper chowning procedure If a program running as root should open a file as another user, don't play stat() and chown() games, instead fork have child process setgid/setuid to the target user open file normally (using O_EXCL is still important) This requires no checks whatsoever in your code - if the user can open it, it works. If the user shouldn't be able to, it won't. No need to chown() the file, etc. ______________________________________________________ Securily changing ownership of a file chown() operates on a file name, and can be vulnerable to a race condition. Instead open() the file in read only mode fstat() the open file descriptor lstat() the file name This may be overkill, depending on the situation Make sure both stat calls point to the file you expect Make sure this is the file you wanted (check uid/etc if needed) fchown() the file descriptor. ______________________________________________________ Securely changing ownership of a file (cont) Verbose example: if ( ! (fd = open( FILENAME, O_RDONLY) ) ) { diehorribly("can't open file"); } fstat(fd, &fstats); lstat(FILENAME, &lstats); if ( fstats.st_dev != lstats.st_dev || fstats.st_ino != lstats.st_ino ) { diehorribly("Somebody's attempting symlink attacks!"); } /* Yes, the file we opened is the one that's there now. */ /* Verify it's what we are expecting */ if ( fstats.st_uid == expected_uid and fstats.st_gid == expected_gid ) { /* do something to it */ fchown( fd, new_uid, new_gid ); } else { diehorribly("Didn't get the file I expected."); } ______________________________________________________ lchown() If you must use chown(), you may instead want lchown() lchown() acts on the link if it's a link Can be used from command line too using the '-h' option: # cd /home/ # mv oldemp newemp/oldemployee_files # cd newemp # chown -h -R newemp oldemployee_files ______________________________________________________ fchmod() Instead of chmod(), use fchmod() whenever possible Open file for reading use fchmod() close file fchown() and fchmod() are not necessary when you are in a directory where You are the only one with write permission, or The sticky bit is set Of course, it's always better to be safe than sorry What if the file permissions of the dir change? What if you use this code in a directory that isn't sticky/etc? ______________________________________________________ Locking with flock() Usage: flock( fd, operation ); Operation is LOCK_SH -- shared lock LOCK_EX -- exclusive lock LOCK_UN -- unlock LOCK_NB -- don't block when attempting to lock flock() never works over NFS, so it should be avoided in those environments. ______________________________________________________ Locking with lockf() Usage: lockf( fd, command, length ); Command is F_LOCK -- exclusive lock F_TLOCK -- nonblocking F_LOCK F_ULOCK -- unlock F_TEST -- check lock status of the file lockf() is actually a library call, that works via fcntl() system call. fcntl() supports NFS locks when available. lockf() supports locking only tail ends of a file, fnctl() provides ability to lock arbitrary regions. ______________________________________________________ Locking drawbacks lockf() and flock() are cooperative locks. Nothing prevents a program from accessing the file even if it's locked. For maximum lockability, you may want to use both. Poorly coded programs can get in a deadlock situation. program a locks file A program b locks file B program a tries to lock file B program b tries to lock file A a and b now block forever... ______________________________________________________ Mandatory locking Mandatory locking is enforced by the kernel Doesn't rely on processes checking for locks. Occur automatically when a program uses flock(), lockf(), or fcntl(). If a program puts a 'read' or 'shared' lock on a file, no other processes can write to it . If a program puts a 'write' or 'exclusive' lock on a file, no other processes can read or write to it. ______________________________________________________ Mandatory locking (cont) Requires that the filesystem is mounted with the 'mand' option: # mount -oremount,mand /var $ mount | grep /var /dev/hda3 on /var type ext3 (rw,mand) Requires that the file to be locked has setgid bit set and doesn't have the group execute bit set: $ ls -l file /dev/hda3 on /var type ext3 (rw,mand) -rw-r-Sr-- 2 reegen reegen 17282 Oct 13 12:17 file For more info about locking, see the man pages, and /usr/src/linux/Documentation/{locks,mandatory}.txt ______________________________________________________ Race conditions in threaded apps Threaded applications can experience race conditions Modification of common variables by multiple threads at once Even "variable++" can be non thread safe! Are usually fixed using mutexes pthread_mutex_lock( mutex ); /* do something */ pthread_mutex_unlock( mutex ); For more info, get a good pthreads book. ;-) ______________________________________________________ System call exit status Every system call has a return value - check them! Usually -1 indicates error On error, the variable 'errno' is set, for example EBADF -- file descriptor not opened for writing EACCES -- access (to file) denied EPERM -- permission (for action) denied EROFS -- read only file system Error message for errno can be printed using perror() perror("Unable to open file foo: "); /* may yield "Unable to open file foo: no such file or directory" */ Or via sys_errlist[] fprintf(stderr, "Error: %s\n", sys_errlist[errno]); Or via strerror() fprintf(stderr, "Error: %s\n", strerror(errno)); errno is cleared after a sucessful system call ______________________________________________________ Ok, some don't. System calls that don't have return values: sigreturn() exit() System calls that always have the same return value: sync() System calls that always succeed, so checking not required: umask() I probably missed some - these are the ones that came to mind... ______________________________________________________ Too lazy to check return values? Feeling lazy? Carpel tunnel got you down? Just don't feel like typing all this? if ( unlink("filename") == -1 ) { diehorribly(); } if ( (bytes = read( fd, buffer, 100 )) < 0 ) { diehorribly(); } if ( ( fd = open( FILENAME, O_RDWR|O_EXCL|O_CREAT, 0666 ) ) == 0 ) { diehorribly(); } ______________________________________________________ Macros to the rescue Try using macros in C: #include #define Chksys0(s) assert( (s) == 0 ) #define Chksys1(s) assert( (s) >= 0 ) And write Chksys0( unlink("filename") ); Chksys0( bytes = read( fd, buffer, 100 ) ); Chksys1( fd = open( FILENAME, O_RDWR|O_EXCL|O_CREAT, 0666 ) ); Chksys1( time( &t ) ); In Perl, return value is usually true/false in a logical way. (Macro idea based on excerpt from "Practical Unix & Internet Security, 3rd Edition", Garfinkel/Spafford/Schwartz. Go buy this book.) ______________________________________________________ Temporary files If all you're worried about is creating a temporary file, there are some handy helpers in C: int mkstemp( template ) Generates unique temporary file name and opens file with O_EXCL and 0600 perms. FILE* tmpfile() Generates unique temporary file name and opens file with O_EXCL and 0600 perms Immediately deletes file after creation. More portable than mkstemp. Functions not to use (vulnerable to race conditions) char* tempnam( dir, prefix ) char* tmpnam( NULL ) char* mktemp( template ) ______________________________________________________ Deleted files Files aren't deleted from the filesystem until All hard links to the file are removed from the filesystem, and No processes have the file open This is great for Temporary files, only used by this process and possibly it's children. Totally unaccessable files. Well, user and root can get at them via /proc/PID/fd/# $ lsof -p 2487 CMD PID USER FD TYPE DEVICE SIZE NODE NAME prog 2487 bri 0u CHR 136,5 7 /dev/pts/5 prog 2487 bri 1u CHR 136,5 7 /dev/pts/5 prog 2487 bri 2u CHR 136,5 7 /dev/pts/5 prog 2487 bri 3r REG 3,5 627 115533 /tmp/foo prog 2487 bri 5u REG 3,5 5571 277062 /tmp/tmpfg3RD3J (deleted) $ ls -l /tmp/tmpfg3RD3J ls: /tmp/tmpfg3RD3J: No such file or directory ______________________________________________________ Investigative /proc entries /proc/PID directories lets you see what's up with processes: cmdline - the command line arguments the program was launched with cwd - a symlink to the current working directory environ - a NULL separated list of environment variables exe - a symlink to the executable fd/# - symlinks to the files open by the process stat/statm/status - various stats in mostly machine readable form. # perl -lpe 's/\0/\n/g' environ | head DISPLAY=:0.0 # grep Uid status Uid: 1000 1000 0 1000 # uid euid suid fsuid # ls -l fd/[35] lrwx------ 1 bad guy 64 Apr 24 21:02 3 -> /tmp/foo l-wx------ 1 bad guy 64 Apr 24 21:02 5 -> /tmp/tmpfg3RD3J (deleted) ______________________________________________________ Investigative /proc entries The /proc filesystem relies on standard Linux file permissions for access control. Users can see all files in their own /proc/PID directories Users can see some files in other's /proc/PID directories (cmdline, status) Root can see everything in /proc/PID directories. Ownership of /proc/PID directories is *not* changed upon setXid! ______________________________________________________ Fun with /proc # ls -l /proc/PID/fd/5 l-wx------ 1 bad guy 64 Apr 24 21:02 5 -> /tmp/tmpfg3RD3J (deleted) # cp /proc/PID/fd/5 /tmp/file # get a copy, even though it's deleted # ls -l /proc/PID/exe l-wx------ 1 bad guy 64 Apr 24 21:02 exe -> /tmp/.secret/prog (deleted) # cp /proc/PID/exe /tmp/recovered_prog ______________________________________________________ Configuration /proc entries There are many /proc hooks into the kernel that can change your system configuration: #/bin/sh # Handy dandy functions enable () { for file in $@; do echo 1> $file; done } disable () { for file in $@; do echo 0> $file; done } disable /proc/sys/net/ipv4/conf/*/accept_source_route enable /proc/sys/net/ipv4/tcp_syncookies disable /proc/sys/net/ipv4/conf/*/accept_redirects enable /proc/sys/net/ipv4/conf/*/secure_redirects disable /proc/sys/net/ipv4/conf/*/send_redirects enable /proc/sys/net/ipv4/conf/*/rp_filter enable /proc/sys/net/ip_always_defrag disable /proc/sys/net/ipv4/ip_forward enable /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts enable /proc/sys/net/ipv4/icmp_echo_ignore_all ______________________________________________________ Changing /proc entries with sysctl Using sysctl and /etc/sysctl.conf # sysctl net.ipv4.ip_forward # just like 1 # cat /proc/net/ipv4/ip_forward # sysctl -w net.ipv4.ip_forward=0 # just like # echo 0 > /proc/net/ipv4/ip_forward # cat /etc/sysctl.conf net.ipv4.ip_forward = 0 net.ipv4.conf.eth0.accept_redirects = 0 # sysctl -p /etc/sysctl.conf # apply all settings in sysctl.conf ______________________________________________________ Using ptrace The ptrace() system call (used by strace program) allows you to observe and control other processes. Other process must be running as you, and never ran setXid, or You must be root Great for monitoring suspect users or potential trojan code: $ strace -eopen ls open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 open("/lib/librt.so.1", O_RDONLY) = 3 open("/lib/libc.so.6", O_RDONLY) = 3 open("/lib/libpthread.so.0", O_RDONLY) = 3 open(".", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 3 open("/etc/mtab", O_RDONLY) = 3 open("/proc/meminfo", O_RDONLY) = 3 $ strace -f -p somepid ______________________________________________________ Using ltrace ltrace does the same thing, but for library calls instead of system calls $ ltrace -eopendir,closedir ls -R >/dev/null opendir(".") = 0x080567c8 closedir(0x080567c8) = 0 qsort(0x08053a50, 16, 116, 0x0804b76c) = opendir("./pictures") = 0x08057908 closedir(0x08057908) = 0 qsort(0x08053a50, 3, 116, 0x0804b76c) = opendir("./movies") = 0x08056808 closedir(0x08056808) = 0 qsort(0x08053a50, 25, 116, 0x0804b76c) = opendir("./movies/naughty") = 0x080579f0 closedir(0x080579f0) = 0 qsort(0x08053a50, 25, 116, 0x0804b76c) = ______________________________________________________ Debuggers The best way to watch what a program attempts to do is to use a debugger. $ gdb /bin/ls (gdb) help List of classes of commands: aliases -- Aliases of other commands breakpoints -- Making program stop at certain points data -- Examining data files -- Specifying and examining files internals -- Maintenance commands obscure -- Obscure features running -- Running the program stack -- Examining the stack status -- Status inquiries support -- Support facilities tracepoints -- Tracing of program execution without stopping the program tui -- Text User Interface commands user-defined -- User-defined commands ______________________________________________________ Debuggers (cont) Debuggers are most useful when Compile-time symbols are still available in the binary. You have time to learn to use debuggers well. You aren't afraid of them. Most languages have a debugger. '/usr/bin/perl -d' for example. ______________________________________________________ Investigating untrusted programs Running any untrusted code to see what it does is a bad idea $ strings unknownprogram ... /ova/ez -es / 2>/qri/ahyy ... $ ./unknownprogram (disk spins a lot...) Little did you know, '/ova/ez -es /' is actually "/bin/rm -rf / 2>/dev/null" encrypted with rot13. When the program runs, it decodes the string and calls it via system(). ______________________________________________________ Running suspicious code more safely There are several things you can do to investigate suspicious code Pull the network cable out and Run under a debugger Run in a chroot jail Copy to a sacrificial system and run there Run in a VMWare or UML system ______________________________________________________ Chroot jails The chroot() system call can change the root for a given program. Excellent way to prevent a program from accessing files outside the chrooted area. Need to copy any necessary files into chroot area /etc/resolv.conf /dev/log /lib/ libraries Root can generally break out of a chroot jail. ______________________________________________________ Chroot example Assuming you've set up a directory /var/convict with all the required libraries, etc, you can run the program /var/convict/prog chrooted in /var/convict, as user 'convict' as follows: #define CONVICT "convict" #define CHROOT_DIR "/var/convict" #define PROG "prog" /* prog is installed in CHROOT_DIR */ int main() { struct passwd *pw = getpwnam( CONVICT ); if ( ! pw ) bail("getpwnam"); if ( chdir(CHROOT_DIR) < 0 ) bail("chdir"); if ( chroot(CHROOT_DIR) < 0 ) bail("chroot"); if ( setgid(pw->pw_gid) < 0 ) bail("setgid"); if ( setuid(pw->pw_uid) < 0 ) bail("setuid"); execl(PROG, PROG, NULL); bail("execl"); } ______________________________________________________ Running code in a VM The best way to run untrusted or untested code is on a replaceable machine or a virtual machine. VMWare Runs on Windows or Linux Can run Linux, Windows, *BSD, or anything that runs on x86 processesors User-Mode-Linux Runs virtual Linux box on your Linux box Both Virtual machine can access host filesystem / network / devices if desired Have independent filesystem in honking files or partition Can have filesystem changes done separate from filesystem itself. Great for kernel development Crashes are unimportant Reboots are fast Restoring filesystem a breeze ______________________________________________________ Executing programs securely How do I exec thee? Let me count the ways: Good choices: execl( const char *path, const char *arg, ...); execle( const char *path, const char *arg , ..., char * const envp[]); execv( const char *path, char *const argv[]); Bad choices: /* doesn't have path hard coded - depends on $PATH variable */ execlp( const char *file, const char *arg, ...); execvp( const char *file, char *const argv[]); /* sends command to shell via "sh -c " -- if user input is included */ /* in the command, they may sneak shell metacharacters in */ /* use pipe / fork / exec instead */ system( const char *string); popen( const char *command, const char *type); ______________________________________________________ Argv[0] The first argument when using exec* is the program name, ala execl( "/bin/sh", "-sh", NULL ); execl( "/bin/ls", "ls", "-r", "/tmp", NULL ); execl( "/bin/gzip", "gunzip", "/tmp/file.gz", NULL ); Great for programs that act differently based on their command name. ______________________________________________________ Proper perl execution Don't use # running program system("/bin/program arg1 arg2"); # getting the output $output = `/bin/ls -lR directory`; open(FOO, "/bin/ls -lR directory|"); # sending input open(FOO, "|/usr/bin/nc webserver 80"); Do use system "/bin/program", "arg1", "arg2"; # getting output open(FOO, '-|') || exec '/bin/ls', '-lR', 'directory'; # sending input open(FOO, '|-') || exec '/usr/bin/nc', 'webserver', '80'; ______________________________________________________ Properl Perl execution Better yet, don't use system or exec in perl at all -- use internals Don't use ls, use fileglobs, stat, and friends. Don't use nc, use IO::Socket. Don't use md5sum, use Digest::MD5. Don't use /usr/sbin/sendmail, use Net::SMTP, etc. etc, etc, etc. If you want to specify argv[0] ($0 in perl) then use exec { "/bin/gzip" } "gunzip", "file.gz"; More helpful open and IPC hints in perldoc perlopen perldoc perlipc ______________________________________________________ Scrubbing the environment Processes you call via exec inherit your environment unless explicitly told not to. Use 'execle' in C -- you can send it a array of environment variables. In Perl, do a fork, clean up %ENV, and then exec; $NEWENV{TERM} = $ENV{TERM}; $NEWENV{EDITOR} = $ENV{EDITOR}; %ENV = %NEWENV; exec "/bin/program", "arg1", "arg2", ... ; Always best to create an empty environment and include necessary values, rather than removing values. ______________________________________________________ Filedescriptor inheritance Exec'd processes inherit file descriptors too. This is necessary for Programs to communicate over pipes Programs to maintain access to TTYs However, this can be a problem if the new process should not be allowed access ot the resource If the new process is running as an unprivileged user If the new process is untrusted / buggy ______________________________________________________ Close-on-exec So, close each unnecessary file descriptor before exec Quite a pain You may accidentally miss some You may accidentally close something important Better yet, set the close-on-exec flag after opening the file fd = open( FILENAME, FLAGS [,mode] ); fcntl( fd, F_SETFD, 1 ); Now, these files will automatically be closed with no work or coding on your part. ______________________________________________________ Limits Each process on Linux can be have limits independent of other processes Amount of CPU time available Maximum file size Availble Memory (data/stack/locked memory/vm limit/resident set size) Coredump size Number of processes Number of open files ______________________________________________________ Limits (cont) In shell: $ ulimit -t unlimited $ ulimit -t 10 $ ulimit -t 10 In C: struct rlimit mylimit; mylimit.rlim_cur = 10; /* time in seconds */ mylimit.rlim_max = 20; setrlimit( RLIMIT_CPU, &mylimit ); getrlimit(RLIMIT_CPU, &mylimit ); printf("current %d maximum %d\n", mylimit.rlim_cur, mylimit.rlim_max ); /etc/security/limits.conf read upon login via PAM. ______________________________________________________ Quotas Filesystems can have usage quotas enforced Soft limits can be exceeded, but User is warned for each subsequent write Cannot exceed soft quota for more than a week Hard limits can never be exceeded Quotas are on a per-user basis Root is not subject to filesystem quotas User-space tools available to view quota status and violators At filesystem creation time, a percentage of the filesystem is reserved for root, regardless of quotas ______________________________________________________ Crypto and Randomness Linux has lots of crypto support OpenSSL libraries SSH remote login/file transfer SSL in Apache / Stunnel / Mail Daeemons / etc PGP with GnuPG (Gnu Privacy Guard) Cryptographic filesystems VPN technologies ______________________________________________________ Randomness All crypto needs good random data. Linux provides /dev/random - truely random data Strong enough for anything Limited to amount of entropy in system Blocks until sufficent entropy available. May want kernel patches to include network entropy for servers. /dev/urandom - strong pseudorandom data. Not sufficiently random for key generation Can be seeded more by writing to /dev/urandom No need for PRNGd or EGD user-space randomness generators ______________________________________________________ Random routines Never, ever, ever, and I mean it, use rand + srand random + srandom erand / drand48 + srand48 nrand / lrand48 + seed48 jrand / mrand48 + lcong48 Instead read /dev/random if you need really strong random numbers read /dev/urandom if you need good pseudo random numbers use the OpenSSL library's RAND_bytes/RAND_pseudo_bytes if you want to be lazy ______________________________________________________ Capabilities The power of root is acually broken up into discrete actions called capabilities. Kernel uses the capable() call, not 'if (uid == 0 )' tests. Capabilities include CAP_SETUID - Allow unrestricted setuid(2) and friends. CAP_SETGID - Allow unrestricted setgid(2) and setgroups(2). CAP_CHOWN - Allow unrestricted use of chown CAP_KILL - Allow signals to be sent to processes you don't own. CAP_NET_BIND_SERVICE Binding low TCP and UDP ports CAP_NET_ADMIN Network configuration (set IP addr, add routes) CAP_NET_RAW - Allow use of raw and packet sockets. CAP_SYS_MODULE - Allow insertion/removal of LKMs ______________________________________________________ Capabilities (cont) By default, all capabilities are provided to any program that runs as root. However The process can drop capabilities if desired Allows it to have just the enhanced privs necessary. The capabilities can be set to be non inheritable Allows it to run other procs without extra privs with less code Capabilities can be removed on a kernel-wide basis No more processes will be able to get those capabilities Many advanced Linux kernel security models manipulate capabilites. Lcap can be used to view or remove capabilities from the running system. ______________________________________________________ Netfilter, et al Very sophisticated kernel packet munging exist 2.4 kernel uses Netfilter (iptables) 2.2 kernel uses IPChains 2.0 kernel uses ipfwadm Can be used to create Network ACLs Firewalls NAT / Masquerade / etc Routers Anything imaginable ______________________________________________________ Netfilter in use A very secure setup: # iptables -F # iptables -P INPUT DROP # iptables -P OUTPUT DROP # iptables -P FORWARD DROP To create a secure and *useful* setup, read Ziegler's "Linux Firewalls, Second Edition" ______________________________________________________ Modules The Linux kernel is monolithic (not micro kernel), but supports LKMS - Loadable Kernel Modules: Device drivers Load only the driver modules you need. Optional code for kernel functions Select only the netfilter modules you need. Kernel code needed for certain user-space functions. Also good for Honeypot monitoring tools Patching/disabling buggy kernel calls without upgrade Rootkits / circumventing security ______________________________________________________ Noptrace module example int (*real_ptrace) (int, int, int, int); int new_ptrace (int, int, int, int); extern void *sys_call_table[]; /* The replacement function */ int new_ptrace(int request, int pid, int addr, int data) { return -1; } int init_module() { /* Save a pointer to the old ptrace function */ real_ptrace = sys_call_table[ __NR_ptrace ]; /* point to our new ptrace function in sys_call_table */ sys_call_table[ __NR_ptrace ] = (void *)new_ptrace; printk(KERN_INFO "noptrace module installed\n"); return 0; } ______________________________________________________ Noptrace module (cont) int cleanup_module() { /* reset the pointer back to the actual function */ sys_call_table[ __NR_ptrace ] = (void *)real_ptrace; printk(KERN_INFO "noptrace module uninstalled\n"); return 0; } ______________________________________________________ Linux kernel source code You've got the whole source code to the kernel, so you can: Modify it to support new hardware Modify it to support new security paradigms Prevent users from running setuid programs unless they're in group 'staff' If uid == 502, allow user to only read files ending in 'e' Modify it to remove any security checks Better speed for embedded systems Use advanced security patches, such as LIDS, GRSecurity, SELinux, RSBAC, OpenBSD's systrace, and more Probably better than rolling your own security patch. ______________________________________________________ Linux Security Module LSM is the the future of alternative Linux security models. Development currently occuring in 2.5 kernels Available as a patch for 2.4 kernels Allows LSM-aware modules to be loaded and unloaded from any LSM-enabled kernel No need to recompile your kernel Can make it easier to support multiple security models Many advanced kernel security patches ported to LSM now Superuser privs Capabilities Domain and Type Enforcement LIDS SELinux OpenWall kernel patches Others 'on their way'... ______________________________________________________ Obligatory plugs http://www.hackinglinuxexposed.com/ Hacking Linux Exposed http://www.buildinglinuxvpns.net Building Linux VPNs http://www.onsight.com/ Onsight, Inc, Linux/Unix/Network Security Consultancy. http://lists.onsight.com/ "Linux Security: Tips, Tricks, and Hackery" weekly newsletter. http://www.hackinglinuxexposed.com/articles/ "Linux Security: Tips, Tricks, and Hackery" archives. ______________________________________________________