CIS 4615 meeting -*- Outline -*- * Failure to handle errors correctly Based on chapter 11 of the book: 24 Deadly Sins of Software Security by M. Howard, D. LeBlanc, and J. Viega (McGraw-Hill, 2010). ** attack ------------------------------------------ ATTACK OVERVIEW 1. discover how to 2. Give input that 3. Exploit the result to cause: ------------------------------------------ ... cause an error condition ... causes that condition to happen ... - denial of service, - revelation of confidential information, e.g., to perform a buffer overflow attack - instability, leading to other attacks ** varieties of the problem *** too much information ------------------------------------------ PROBLEM: TOO MUCH INFORMATION (INFO. LEAKAGE) Telling the user about ------------------------------------------ ... exactly what error occurred, with debugging information Q: Do normal users want to see all that debugging information? No, it only helps developers and attackers! We'll discuss this further in the next section *** ignoring errors This is especially a problem in C, which doesn't have exception handling built in. ------------------------------------------ PROBLEM: IGNORING ERRORS Ignoring return values (in C) or exceptions that indicate errors Examples: Can this Windows code cause problems? ImpersonateSelf(Client); /* ... carry on as the client ...*/ RevertToSelf(); In C, can this cause any problems? FILE* lf = fopen(logfile, "a"); fprintf(lf, "%s\n", entry); fclose(lf); ------------------------------------------ ... Yes, if the call to ImpersonateSelf fails, then the process still has the identity (impersonation token) it had before, so could give the client more privileges than desired. Similar to CVD-2004-0077: The do_mremap function for the mremap system call in Linux 2.2 to 2.2.25, 2.4 to 2.4.24, and 2.6 to 2.6.2, does not properly check the return value from the do_munmap function when the maximum number of VMA descriptors is exceeded, which allows local users to gain root privileges! ... Yes, the problem is that the fopen may fail, due to ignored return codes, from fopen especially The file manipulating C code is in badlogging.c, try it after doing touch logfile.log; chmod 000 logfile.log and it will have a segmentation fault... A corrected version is in logging.c: /* $Id$ */ #include #include #include #define DEBUG 1 static void quit(const char * msg) { if (DEBUG) { perror(msg); } exit(1); } int main() { char *logfile = "logfile.log"; FILE* lf = fopen(logfile, "a"); char *entry = "critical info"; if (lf == NULL) { quit("cannot open log file"); } else { size_t es = strlen(entry); size_t writn = fprintf(lf, "%s\n", entry); if (writn != es+1) { quit("write to log file failed"); } if (fclose(lf) != 0) { quit("closing log file failed"); } } return 0; } *** misinterpreting errors ------------------------------------------ MININTERPRETING ERRORS recv() for getting socket data >0 means length of message in bytes =0 means no messages available -1 means error malloc() =0 is okay if size argument was 0 NULL if size argument was >0 and out of memory realloc() NULL if size argument is 0 and okay NULL if size argument is >0 and not enough memory fgets() NULL if there is an error NULL if the no data are read MulDiv() windows 64 bit integer function -1 on error ------------------------------------------ Q: What problems do these return conventions pose? Possible to write code that is not perfectly correct, and attackers can take advantage of unstable situations. ** remediation ------------------------------------------ REMEDIATION In C be sure to check return values when appropriate In Java/C# don't ignore mandatory exceptions Code reviews are good for spotting problems Look especially for: - C code that does not check return values - Windows code that does not check return values of impersonation functions Use Microsoft Visual C++ annotation _Check_return_ or Java checked exceptions to force caller to check for the return value (C++) or handle exceptions (Java) ------------------------------------------ ------------------------------------------ WHEN TO USE CHECKED EXCEPTIONS I.e., when to force users to handle exceptions? When it is not usually possible or easy for caller to ------------------------------------------ ... know if the call will run into a problem Q: What are some examples where checked exceptions are appropriate? I/O - don't know when EOF or errors will happen user interaction - users are unpredictable the type hides information needed to know if an error will occur