CYSE 411/AIT681 Secure Software Engineering Topic #10. Secure Coding: Integer Security Instructor: Dr. Kun Sun 1
This lecture: [Seacord]: Chapter 5 Readings 2
Secure Coding String management Pointer Subterfuge Dynamic memory management Integer security Formatted output Race conditions Web security 3
Introductory Example 1. int main(int argc, char *argv[]) { 2. unsigned short int total; 3. total = strlen(argv[1])+ 4. strlen(argv[2])+1; 5. char *buff = (char *)malloc(total); 6. strcpy(buff, argv[1]); 7. strcat(buff, argv[2]); 8. } Can you find the error? 4
Introductory Example Accepts two string arguments and calculates their combined length (plus an extra byte for the terminating null character) int main(int argc, char *const *argv) { } unsigned short int total; total = strlen(argv[1]) + strlen(argv[2]) + 1; char *buff = (char *) malloc(total); strcpy(buff, argv[1]); strcat(buff, argv[2]); Memory is allocated to store both strings. The 1 st argument is copied into the buffer and the 2 nd argument is concatenated to the end of the 1 st argument 5
Vulnerability An attacker can supply arguments such that the sum of the lengths of the strings cannot be represented by the unsigned short int total. The strlen() function returns a result of type size_t, an unsigned long int on IA-32 As a result, the sum of the lengths + 1 is an unsigned long int. This value must be truncated to assign to the unsigned short int total. If the value is truncated malloc() allocates insufficient memory and the strcpy() and strcat() will overflow the dynamically allocated memory 6
Integers Integer range checking has not been systematically applied in the development of most C and C++ software. security flaws involving integers exist a portion of these are likely to be vulnerabilities A software vulnerability may result when a program evaluates an integer to an unexpected value. 7
Signed and Unsigned Types Integers in C and C++ are either signed or unsigned. For each signed type there is an equivalent unsigned type. Signed integers are used to represent positive and negative values. On a computer using two s complement arithmetic, a signed integer ranges from -2 n-1 through 2 n-1-1. 8
One s Complement Negative numbers are represented in one s complement form by complementing each bit even the sign bit is reversed 0 0 1 0 1 0 0 1 1 1 0 1 0 1 1 0 each 1 is replaced with a 0 each 0 is replaced with a 1 9
Two s Complement The two s complement form of a negative integer is created by adding one to the one s complement representation. 0 0 1 0 1 0 0 1 0 0 1 0 1 0 0 1 1 1 0 1 0 1 1 0 + 1 = 1 1 0 1 0 1 1 1 Two s complement representation has a single (positive) value for zero. The sign is represented by the most significant bit. 10
Signed Integer Representation 11
Unsigned Integers Unsigned integer values range from zero to a maximum that depends on the size of the type. Maximum value can be calculated as 2 n -1, where n is the number of bits used to represent the unsigned type. For each signed integer type, there is a corresponding unsigned integer type. 12
Unsigned Integer Representation two s complement 13
Standard Integer Types Standard integers include the following types, in non-decreasing length order signed char short int int long int long long int 14
Integer Ranges Minimum and maximum values for an integer type depend on the type s representation signedness number of allocated bits 15
Example Integer Ranges signed char unsigned char -128 0 127 short 0 255-32768 unsigned short 0 32767 0 65535 16
Integer Conversions Type conversions occur explicitly in C and C++ as the result of a cast or implicitly as required by an operation. Conversions can lead to lost or misinterpreted data. Implicit conversions are a consequence of the C language ability to perform operations on mixed types. 17
Rule 1: Integer Promotions Integer types smaller than int are promoted when an operation is performed on them. If all values of the original type can be represented as an int the value of the smaller type is converted to int otherwise, it is converted to unsigned int. Integer promotions are applied as part of the usual arithmetic conversions to certain argument expressions operands of the unary +, -, and ~ operators operands of the shift operators 18
Integer Promotion Example Integer promotions require the promotion of each variable (c1 and c2) to int size char c1, c2; c1 = c1 + c2; The two ints are added and the sum truncated to fit into the char type. Integer promotions avoid arithmetic errors from overflow of intermediate values. 19
Integer Promotion Example 1. int _tmain(int argc, _TCHAR* argv[]) 2. { 3. 004017E0 push ebp 4. 004017E1 mov ebp,esp 5. 004017E3 push ecx 6. char c1 = 'a'; 7. 004017E4 mov byte ptr [c1],61h 8. char c2 = 'b'; 9. 004017E8 mov byte ptr [c2],62h 10. char c3 = c1+c2; 11. 004017EC movsx eax,byte ptr [c1] ;Move with sign extension 12. 004017F0 movsx ecx,byte ptr [c2] ;Move with sign extension 13. 004017F4 add eax,ecx 14. 004017F6 mov byte ptr [c3],al ;Move with truncation 15. } 16. 004017F9 xor eax,eax 17. 004017FB mov esp,ebp 18. 004017FD pop ebp 19. 004017FE ret 20
Implicit Conversions 1. char cresult, c1, c2, c3; 2. c1 = 100; 3. c2 = 90; 4. c3 = -120; 5. cresult = c1 + c2 + c3; The sum of c1 and c2 exceeds the maximum size of signed char However, c1, c1, and c3 are each converted to integers and the overall expression is successfully evaluated. The sum is truncated and stored in cresult without a loss of data The value of c1 is added to the value of c2. 21
Rule 2: Integer Conversion Rank Every integer type has an integer conversion rank that determines how conversions are performed. 22
Integer Conversion Rank Rules No two signed integer types have the same rank, even if they have the same representation. The rank of a signed integer type is > the rank of any signed integer type with less precision. The rank of long long int is > the rank of long int, which is > the rank of int, which is > the rank of short int, which is > the rank of signed char. The rank of any unsigned integer type is equal to the rank of the corresponding signed integer type. 23
Signed Integer Conversion Example 1. unsigned int k = ULONG_MAX; 2. char c = -1; 3. if (c == k) { 4. printf("-1 = 4,294,967,295?\n"); 5. } Because of integer promotions, c is converted to an unsigned integer with a value of 0xFFFFFFFF or 4,294,967,295 The value of c is compared to the value of k. 24
Usual Arithmetic Conversions 1. If both operands have the same type no conversion is needed. 2. If both operands are of the same integer type (signed or unsigned), the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank. 3. If the operand that has unsigned integer type has rank >= to the rank of the type of the other operand, the operand with signed integer type is converted to the type of the operand with unsigned integer type. 4. If the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type is converted to the type of the operand with signed integer type. 5. Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type. 25
Integer Error Conditions Integer operations can resolve to unexpected values as a result of an overflow sign error truncation 26
Overflow An integer overflow occurs when an integer is increased beyond its maximum value or decreased beyond its minimum value. Overflows can be signed or unsigned A signed overflow occurs when a value is carried over to the sign bit An unsigned overflow occurs when the underlying representation can no longer represent a value 27
Overflow Examples 1. int i; 2. unsigned int j; 3. i = INT_MAX; // 2,147,483,647 4. i++; 5. printf("i = %d\n", i); 6. j = UINT_MAX; // 4,294,967,295; 7. j++; 8. printf("j = %u\n", j); i=-2,147,483,648 j = 0 28
Overflow Examples 9. i = INT_MIN; // -2,147,483,648; 10. i--; 11. printf("i = %d\n", i); i=2,147,483,647 12. j = 0; 13. j--; 14. printf("j = %u\n", j); j = 4,294,967,295 29
Integer Overflow Example 1. void getcomment(unsigned int len, char *src) { 2. unsigned int size; 3. size = len - 2; 4. char *comment = (char *)malloc(size + 1); 5. memcpy(comment, src, size); 6. return; } 0 byte malloc() succeeds Size is interpreted as a large positive value of 0xffffffff int _tmain(int argc, _TCHAR* argv[]) { } getcomment(1, "Comment "); return 0; Possible to cause an overflow by creating an image with a comment length field of 1 30
Truncation Errors Truncation errors occur when an integer is converted to a smaller integer type and the value of the original integer is outside the range of the smaller type Low-order bits of the original value are preserved and the high-order bits are lost. 31
Truncation Error Example 1. char cresult, c1, c2; 2. c1 = 100; 3. c2 = 90; 4. cresult = c1 + c2; Adding c1 and c2 exceeds the max size of signed char (+127) Truncation occurs when the value is assigned to a type that is too small to represent the resulting value Integers smaller than int are promoted to int or unsigned int before being operated on 32
Truncation Error Example bool func(char *name, long cbbuf) { } unsigned short bufsize = cbbuf; char *buf = (char *)malloc(bufsize); if (buf) { memcpy(buf, name, cbbuf); } if (buf) free(buf); return true; return false; cbbuf is used to initialize bufsize which is used to allocate memory for buf cbbuf is declared as a long and used as the size in the memcpy() operation 33
Truncation Vulnerability cbbuf is temporarily stored in the unsigned short bufsize. The maximum size of an unsigned short for both GCC and the Visual C++ compiler on IA- 32 is 65,535. The maximum value for a signed long on the same platform is 2,147,483,647. A truncation error will occur on line 2 for any values of cbbuf between 65,535 and 2,147,483,647. 34
Sign Errors Converting an unsigned integer to a signed integer of Equal size - preserve bit pattern; high-order bit becomes sign bit Greater size - the value is zero-extended then converted Lesser size - preserve low-order bits If the high-order bit of the unsigned integer is Not set - the value is unchanged Set - results in a negative value 35
Sign Errors Converting a signed integer to an unsigned integer of Equal size - bit pattern of the original integer is preserved Greater size - the value is sign-extended then converted Lesser size - preserve low-order bits If the value of the signed integer is Not negative - the value is unchanged Negative - the result is typically a large positive value 36
Sign Error Example 1. int i = -3; 2. unsigned short u; 3. u = i; 4. printf("u = %hu\n", u); Implicit conversion to smaller unsigned integer There are sufficient bits to represent the value so no truncation occurs. The two s complement representation is interpreted as a large signed value, however, so u = 65533 37
Sign Error Example #define BUFF_SIZE 10 int main(int argc, char* argv[]){ int len; char buf[buff_size]; } len = atoi(argv[1]); if (len < BUFF_SIZE){ memcpy(buf, argv[2], len); } Program accepts two arguments (the length of data to copy and the actual data) len declared as a signed integer argv[1] can be a negative value A negative value bypasses the check Value is interpreted as an unsigned value of type size_t 38
Upcast Example void* AllocBlocks(size_t cblocks) { // allocating no blocks is an error if (cblocks == 0) return NULL; // Allocate enough memory // Upcast the result to a 64-bit integer // and check against 32-bit UINT_MAX // to makes sure there's no overflow ULONGLONG alloc = cblocks * 16; } return (alloc < UINT_MAX)? malloc(cblocks * 16): NULL; Can you find the error? 39
Result Always > UINT_MAX void* AllocBlocks(size_t cblocks) { // allocating The no result blocks is assigned is to an a ULONGLONG error but the calculation may have already overflowed. if (cblocks == 0) return NULL; // Allocate enough memory // Upcast the result to a 64-bit integer // and check against 32-bit UINT_MAX // to makes sure there's no overflow ULONGLONG alloc = cblocks * 16; return (alloc < UINT_MAX)? malloc(cblocks * 16): NULL; } This is a 32-bit operation that results in a 32-bit value. 40
Corrected Upcast Example void* AllocBlocks(size_t cblocks) { // allocating no blocks is an error if (cblocks == 0) return NULL; // Allocate enough memory // Upcast the result to a 64-bit integer // and check against 32-bit UINT_MAX // to makes sure there's no overflow ULONGLONG alloc = (ULONGLONG)cBlocks*16; return (alloc < UINT_MAX)? malloc(cblocks * 16) : NULL; } 41
Source Code Audit Source code should be audited or inspected for possible integer range errors When auditing, check for the following: Integer type ranges are properly checked. Input values are restricted to a valid range based on their intended use. Integers that do not require negative values are declared as unsigned and properly range-checked for upper and lower bounds. Operations on integers originating from untrusted sources are performed using a safe integer library. 42