Software Security
Diomidis Spinellis
Department of Management Science and Technology
Athens University of Economics and Business
Athens, Greece
dds@aueb.gr
Principles for Software Security
- Secure the weakest link
Example:
social engineering attacks; attacks agains applications vs firewalls
- Defense in depth
Example:
use a VPN and encrypt your data
- Fail securely
Counterexample:
Pressing Esc on the Windows 95/98/Me login screen
(with domain login dissabled)
- Principle of least Privilege
Counterexample:
Unix programs running with superooser privileges.
Example: sendmail submission user.
- Compartmentalize
Counterexample:
Outlook HTML rendering engine.
- Keep it simple
Example:
choke point e.g. firewall.
Counterexample: security protocols, they are complex by neccessity, but
flaws are often found years after their introduction.
- Do not volunteer information
Example:
$ telnet relay2.ucia.gov smtp
Trying 198.81.129.194...
Connected to relay2.ucia.gov.
Escape character is '^]'.
220 ain-relay2.net.cia.gov SMTP Ready.
HELO aueb.gr
250 (aueb.gr) pleased to meet you.
Counterexample:
$ telnet mail.aueb.gr smtp
Trying 195.251.255.142...
Connected to hermes.aueb.gr.
Escape character is '^]'.
220 hermes.aueb.gr ESMTP Sendmail 8.12.9/8.12.9; Fri, 4 Jul 2003 12:53:14 +0300
HELO aueb.gr
250 hermes.aueb.gr Hello istlab.dmst.aueb.gr [195.251.253.207], pleased to meet you
- Hiding secrets is close to impossible
Example:
DVD DeCSS
- Be reluctant to trust
Counterexample:
downloading software
- Use community resources
Example:
public cryptographic algorithms and their implementations.
Buffer Overflows
- Cause of around 20-50% of all CERT security alerts
- Simple example (DOS attack)
$ cat bufftest.c
main()
{
char buff[20];
gets(buff);
puts(buff);
}
$ cc -o bufftest bufftest.c
/tmp/ccjCiHlw.o: In function `main':
/tmp/ccjCiHlw.o(.text+0xe): warning: this program uses gets(), which is unsafe.
$ ./bufftest
warning: this program uses gets(), which is unsafe.
normal text
normal text
$ ./bufftest
warning: this program uses gets(), which is unsafe.
text longer than the buffer size
text longer than the buffer size
Segmentation fault (core dumped)
How does a Buffer Overflow Work?
The attacker can execute code by corrupting the stack
This is the stack when gets is called:
+---------------+
| envp | Program's environment
+---------------+
| argv | Argument vector
+---------------+
| argc | Argument count
+---------------+
| _start | Return address of main
+---------------+
| buff[0] | First byte of buffer (e.g. 'n')
+---------------+
| buff[1] | Second byte of buffer (e.g. 'o')
+---------------+
| buff[...] | More buffer bytes
+---------------+
| buff[19] | Last byte of buffer
+---------------+
| main+12 | Return address of gets
+---------------+
This is the corrupted stack after an attack:
+---------------+
| envp | Program's environment
+---------------+
| argv | Argument vector
+---------------+
| argc | Argument count
+---------------+
| _start | Return address of main
+---------------+
| buff[0] |<-+ First byte of buffer (EVIL CODE)
+---------------+ |
| buff[1] | | Second byte of buffer (EVIL CODE)
+---------------+ |
| buff[...] | | More buffer bytes (more EVIL CODE)
+---------------+ |
| buff[19] | | Last byte of buffer
+---------------+ |
| &buff[0] |--^ Overwritten return address
+---------------+
Buffer Overflow Defences
- Use a language with strong typing and array checking
- Avoid fixed buffers
- Avoid unchecked routines using fixed buffers (e.g. gets, strcpy)
- Use routines that have buffer length as an argument (e.g. fgets, strncpy)
- Use libraries and execution environments that protect against stack
smashing attacks
- When writing to a fixed buffer, check index against the buffer's size
Unix Access Control
- Every user has a (typically unique) identifier
- Each user belongs to one or more groups
Example:
$ id
uid=1000(dds) gid=1000(dds) groups=1000(dds), 0(wheel), 10000(cvs), 20000(lh), 20001(dynweb), 20002(rpipe), 20003(postg), 20004(issues), 20005(dewdrop), 20006(eaware), 20007(mexpress), 20008(ivm), 20009(weblog), 20010(rng), 20011(uca)
- Each file belongs to a user and a group
- Each file or directory has a set of permissions associated with it
- Permissions are: write access, read access, execute permission
- A different permission set is specified for the file's user, group,
and all other users.
- On directories execute permission implies ability to traverse
- Executable files can be specified to run under the permission of their
owner or group (rather than the user executing them).
- A sepcial user, the super user (named root overrides all
access permissions
Example:
-r-xr-xr-x 1 root wheel 206740 Mar 27 15:42 /usr/bin/make
-r-sr-xr-x 1 man wheel 29752 Mar 27 15:39 /usr/bin/man
-r-sr-xr-x 2 root wheel 28828 Mar 27 15:42 /usr/bin/passwd
drwxr-xr-x 23 root wheel 1024 Jun 15 16:38 /usr/src
drwxrwxrwt 3 root wheel 512 Jul 4 13:24 /usr/tmp
drwxr-xr-x 2 root wheel 512 Dec 2 2002 /usr/var
drwxr-xr-x 13 root wheel 512 Jun 15 17:01 /usr/www
-rw------- 1 root wheel 5291 Jul 2 12:47 /etc/master.passwd
Windows Access Control
- Users belong to groups
- Preconfigured groups with useful default permissions
- Permissions to files are fine grained:
- File permissions can be specified for users, groups, or abstract entities
- Can also specify auditing on an element basis
- Audit can generate log entries for success or failure
- A policy editor allows the centralized specification of the security policy
- Comprehensive, but complex system.
Few users and administrators understand it and use it correctly.
Race Conditions
- Expose a window of vulnerability
- Time of check different from time of use (TOCTOU)
and not an atomic operation
- Vulnerable:
- System objects (e.g. files) accessed across system calls (not atomic operations)
- Program objects (e.g. variables) in multithreaded programs
- Example:
if (can_access(file))
/* Window of vulnerability */
write_new_conents(file)
The attacker can:
$ touch my_file
$ privileged_program my_file &
$ rm my_file # Must be executed within the window
$ ln -s /etc/passwd my_file # Must be executed within the window
- Countermeasures:
- Use atomic operations (e.g. open/rename can create/rename a file or fail if the target file exists)
- Use locking in multithreaded programs (e.g. synchronized in Java code)
Problematic APIs
- Functions Susceptible to Buffer Overflows
- Format String Vulnerabilities
- Path and Shell Metacharacter Vulnerabilities
- Temporary Files
- Functions Unsuitable for Cryptographic Use
- Forgeable Data
Randomness and Determinism
Applying Cryptography
- Do not implement functions from scratch
- Use a crypto API (e.g. OpenSSL, Microsoft CryptoAPI)
- Careful on how you apply the primitives (e.g. avoid DES ECB mode)
- Careful on how you manage your keys
- Destroy data and keys when no more needed
Trust Management
- Trust is transitive
- Do not trust other programs you invoke (e.g. editor from a restricted shell)
- Do not trust input you do not control (e.g. hidden web fields)
- Do not trust code you do not control (e.g. Javascript validation)
- Be careful with metacharacters and interpreted languages
(Perl, SQL, sh, PHP, ASP)
Untrusted Input
- Environment variables
- Network data (e.g. DNS)
Result Verification
Failing to check the success of the Unix system calls
setuid
setegid
and
setgroups
or the Windows API calls
RpcImpersonateClient
ImpersonateLoggedOnUser
CoImpersonateClient
ImpersonateNamedPipeClient
ImpersonateDdeClientWindow
ImpersonateSecurityContext
ImpersonateAnonymousToken
ImpersonateSelf
and
SetThreadToken
can result in a process running with elevated privileges.
Data and Privilege Leakage
Often consequence of discretionary access control.
- Data leakage
- Privilege leakage
- The Java approach
- Isolating privileged code
Password Authentication
- Check for good passwords when they are selected.
Dissallow:
- Short passwords
- Username appearing in password
- Only digits
- Only numbers
- Words in the dictionary (uppercase, lowercase, mixed)
- Do not store the encrypted password;
use the password to encrypt a known key.
- Protected the password databse to prevent dictionary attacks.
- Keep track of failed login attempts
- Even better, use variable passwords like SecureID
Database Security
- Communication is typically unencrypted, therefore restrict it
beyond the firewall.
- Enforce database level security
- Create database users
- Grant access for specific objects and actions (SELECT, INSERT, DELETE, UPDATE)
- Use separate users and permissions for web and maintenance access.
- Careful where you store the database access passwords (client applications)
- Keep in mind statistical attacks
Application Security
Copy protection measures:
- Floating network licenses
- Hardware dongles
- Node locking
Tamperproofing:
- Add antidebugging measures
- Checksum the code
- Plant decoys
- Avoid direct responses
- Code obfuscation
- Code and data encryption
Keep in mind that the attacker has complete control of your application.
Bibliography
- Matt Bishop and
Michael Dilger.
Checking for race conditions in file accesses.
Computing Systems, 9(2):131–152, Spring 1996.
- Michael Howard and
David LeBlanc.
Writing Secure Code.
Microsoft Press, Redmond, WA, second edition, 2003.
- Gary McGraw and
Edward W. Felten.
Securing Java: Getting Down to Business with Mobile Code.
Wiley, New York, 1999.
- Diomidis Spinellis.
Reflection as a mechanism for software integrity verification (http://www.dmst.aueb.gr/dds/pubs/jrnl/1999-TISS-Reflect/html/reflect.html).
ACM Transactions on Information and System Security, 3(1):51–62,
February 2000.
- John Viega and Gary
McGraw.
Building Secure Software: How to Avoid Security Problems the Right Way.
Addison-Wesley, 2001.
- J. Viega, J. T. Bloch,
Y. Kohno, and G. McGraw.
Its4: A static vulnerability scanner for c and c++ code.
In Proceedings of the 16th Annual Computer Security Applications
Conference (ACSAC'00), page 257. IEEE Computer Society, 2000.