Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Security Injection Assessments: Buffer Overflow Assessments: Objective 1: Describe the vulnerability (CS0, CS1, CS2) 1.1 What is a buffer overflow vulnerability? Answer: Buffer overflow occurs when data is written beyond the bounds of an object. A buffer overflow vulnerability occurs when instructions in a program access an object without first verifying that the access will not go out of bounds 1.2 Which data types are most susceptible to buffer overflow problems? Answer: Arrays and other static, fixed-size objects 1.3 For the following code, assume nums is an array of 10 integers,and b is any integer variable. Which of the following code snippets might lead to a vulnerability, and why? a. for (int i = 0; i <= 10; i++) { nums[i] = b; } b. nums[b] = 10; Answer: a) Might lead a vulnerability because the last element in nums is found at index 9, and the loop will attempt to store a value at index 10. b) is also a vulnerability, as the value of the index b might be outside the bounds of a legal index (0-9, inclusive). 1.4 Write a few lines of code that might lead to values being stored outside of the bounds of an array. Java: int[] a = new a[10]; for (int i =0; i < 10; i++) { a[i] = i; } Scanner scan = new Scanner(System.in); System.out.print(“Which item do you want?’’); int x = scan.nextInt(); System.out.print(“your value is ’’+a[x]); C++: int a[10]; int x; for (int i =0; i < 10; i++) { a[i] = i; } cout << “Which item do you want?’’; cin >> x; cout << “your value is ’’ << a[x] << endl; Objective 2: Describe potential problems that may result from the vulnerability (CSO, CS1, CS2) 2.1 Your program has a procedure that writes 1000 values to an integer array that is provided as a parameter. What might happen if you called this procedure with an integer array containing room for only 100 values? Answer: The first 100 values will be stored correctly, but the remaining 900 will be written beyond the bounds of the array, causing a buffer overflow. The program may crash, or may present a vulnerability that might be exploited. 2.2 Some programs that store user input place an arbitrary limit on the number of characters that may be typed by the user. Any input that does not fit within these bounds is truncated: characters that do not fit in the available space are simply discarded. Does this approach eliminate vulnerabilities? Are there any potential concerns associated with this strategy? Answer: It might or might not eliminate vulnerabilities, depending on how it is written. If the input is stored in a very large buffer and then copied into the file buffer ( of the appropriate size), or if characters are added one at a time to a fixed-length buffer, the vulnerability may be avoided. If this is done improperly, the vulnerability may still exist. This strategy may be inappropriate in any case, as it might cause loss of information. If a user is only given 10 characters to store a phone number, any spaces, parentheses, or dashes will make it impossible to store the input in the number of characters allowed, so some digits will be lost. 2.3 Data buffers are often stored in adjacent locations in memory. For example, if you allocate an array of 10 integers, followed by an array of 10 strings, the strings might be found in memory locations immediately following the locations allocated for the integers. What might happen if there was a buffer overflow in the integer buffer? Answer: In some languages, such as Java, the program would throw an exception. In others – namely C and C++ - the integer data would be written into the memory allocated for the string array. This might cause garbage data to be written, which could lead to errors or vulnerabilities. Objective 3: Identify vulnerabilities in a simple program written in the language of instruction (CS0, CS1,CS2) Three short programs that modify buffer contents in ways that might cause overflows. If simpler exercises are desired, the print and fill array routines may be used without the accompanying code. Note: Programs below have vulnerabilities marked. Remove vulnerability comments before assig ning to students. 3.1 Shifting contents of a buffer. Java: public class ShiftBuffer { public static final int SIZE=10; public static void main(String[] args) { int[] vals = new int[5]; for (int i = 0; i < SIZE;i++) { vals[i] = i*i; //VULNERABILITY } System.out.println("Original"); printBuffer(vals); System.out.println("Shifted"); shiftBuffer(vals,3); printBuffer(vals); } public static void printBuffer(int[] vals ) { for (int i=0; i < SIZE; i++) { System.out.println(vals[i]+" "); //VULNERABILITY } System.out.println(); } public static void shiftBuffer(int[] vals,int x) { for (int i = SIZE-1; i >=0; i--) { // shift values to right by x vals[i+x] = vals[i]; VULNERABILITY } } } C++: #include <iostream> using namespace std; void printBuffer(int []); void shiftBuffer(int [],int); const int SIZE=10; int main() { int vals[5]; for (unsigned int i = 0; i < SIZE;i++) { vals[i] = i*i; //VULNERABILITY } cout << "Original" << endl; printBuffer(vals); cout << "Shifted" << endl; shiftBuffer(vals,3); printBuffer(vals); } void printBuffer(int vals[]) { for (unsigned int i=0; i < SIZE; i++) { cout << vals[i] << " "; //VULNERABILITY } cout << endl; } void shiftBuffer(int vals[],int x) { for (unsigned int i = SIZE-1; i >=0; i--) { // shift values to right by i vals[i+x] = vals[i]; //VULNERABILITY } } 3.2 Reversing contents of a buffer Java: import java.util.Scanner; public class ReverseBuffer { public static void main(String[] args) { int[] vals = fillArray(10); int[] reverse = reverseArray(vals); System.out.println("Original: "); printVals(vals); System.out.println("Reversed: "); printVals(reverse); } public static int[] fillArray(int sz) { int[] a = new int[sz]; for (int i =0; i <=sz; i++) { //VULNERABILITY a[i] =i+2; } return a; } public static void printVals(int[] vals ) { for (int i=0; i <= vals.length; i++) { System.out.println(vals[i]+" "); //VULNERABILITY } System.out.println(); } public static int[] reverseArray(int[] vals) { int[] rev = new int[vals.length]; int j = 0; for (int i =vals.length; i >=0; i--) { //VULNERABILITY rev[j] = vals[i]; j++; } return rev; } } C++: #include <iostream> using namespace std; void fillArray(int[],int); void reverseArray(int[],int[],int); void printVals(int[],int); int main() { int vals[10]; int reverse[10]; fillArray(vals,20);//VULNERABILITY reverseArray(vals,reverse,20);//VULNERABILITY cout << "Original: " << endl;; printVals(vals,20); cout << "Reversed: " << endl; printVals(reverse,20); return 0; } void fillArray(int a[],int sz) { for (unsigned int i =0; i <=sz; i++) { a[i] =i+2; } //VULNERABILITY } void printVals(int vals[],int sz) { for (unsigned int i=0; i <= sz; i++) { //VULNERABILITY cout << vals[i] <<" "; } cout << endl; } void reverseArray(int src[],int dest[], int sz) { int j = 0; for (unsigned int i =sz; i >=0; i--) {//VULNERABILITY dest[j] = src[i]; j++; } } 3.3 Copying contents of a buffer twice into a new buffer. Java: public class CopyBuffer { public static final int SIZE=10; public static void main(String[] args) { int[] vals = fillArray(SIZE); int[] doubled = doubleArray(vals); System.out.println("Original"); printBuffer(vals); System.out.println("Shifted"); printBuffer(doubled); } public static int[] fillArray(int sz) { int[] a = new int[sz]; for (int i =0; i <=sz; i++) { //VULNERABILITY a[i] =i*5; } return a; } public static int[] doubleArray(int[] vals) { int[] d = new int[vals.length]; int j = 0; for (int rep = 0; rep < 2; rep++) { for (int i =0; i <= vals.length; i++) { d[j] =vals[i]; //VULNERABILITY j++; } } return d; } public static void printBuffer(int[] b ) { for (int i=0; i <= b.length; i++) { //VULNERABILITY System.out.println(b[i]+" "); } System.out.println(); } } C++: #include <iostream> using namespace std; const int SIZE=10; void fillArray(int[],int); void doubleArray(int[],int ,int[]); void printBuffer(int[],int sz); int main() { int vals[SIZE]; int doubled[SIZE]; fillArray(vals,SIZE); doubleArray(vals,SIZE,doubled); cout << "Original: " << endl; printBuffer(vals,SIZE); cout << "Doubled: "<<endl; printBuffer(doubled,SIZE); } void fillArray(int a[],int sz) { for (unsigned int i =0; i <=sz; i++) { a[i] =i*5; } //VULNERABILITY } void doubleArray(int src[],int sz,int dest[]) { int j = 0; for (unsigned int rep = 0; rep < 2; rep++) { for (unsigned int i =0; i <= sz; i++) { //VULNERABILITY dest[j] =src[i]; j++; } } } void printBuffer(int vals[],int sz) { for (unsigned int i=0; i <= sz; i++) { cout << vals[i] <<" "; //VULNERABILITY } cout << endl; } Objective 4: Discuss general strategies for mitigating vulnerabilities (CS1, CS2) 4.1 You're writing some code that will make extensive use of arrays. Name two approaches you should use in your code to ensure that you don't go out of bounds when you use the arrays. Answer: make sure the array has enough space to hold the number of objects that you will need to work with, validate indices, use buffer/array-size accessors (if available) to determine the maximum allowable index, and consider using alternative data structures that reduce risks – such as ArrayList or Vector instead of array. 4.2 A friend suggests the following approach for avoiding buffer problems: “Just allocate 10 x as much space as you could ever possibly need?” That way, you’ll always have enough space, and you’ll never go outside the bound s of the array.” Is this a good strategy? Will it eliminate all of the problems that might result from an out-ofbounds array access? Answer: This is probably not a good strategy. Allocating vast amounts of memory is inefficient and may cause your program or otherwise fail. In some cases, such over-allocation may be insufficient. Whenever possible, your program should determine how much space is needed before allocating storage. 4.3 Are exception handling facilities a sufficient tool for eliminating buffer overflows? Why or why not? Answer: Exception handling tools may help, but they are not sufficient. Some languages such as C and C++ - do not generate exceptions when an out-of-bound array access occurs. Furthermore, exceptions must be handled properly, which may add complexity to code and cause overhead in processing. Carefully-written code that avoids buffer possible overflows is generally preferable. Objective 5: Write code that uses appropriate techniques to mitigate or avoid the vulnerability (CS1,CS2) 5.1 Write a routine that will put the characters in a string into a character array (only the characters – not any termination or null characters). Your routine should create and return a new array to store the characters. Answer: Java: import java.util.Scanner; public class CopyString { public static void main(String[] args) { String s = getString(); char[] foo = copyString(s); for (int i = 0; i < foo.length; i++) { System.out.print(foo[i]); } System.out.println(); } public static String getString() { Scanner s = new Scanner(System.in); System.out.print("Please type a string: "); String str = s.nextLine(); return str; } public static char[] copyString(String s) { char[] copy = new char[s.length()]; for (int i = 0; i < s.length(); i++) { copy[i]=s.charAt(i); } return copy; } } C++: #include <iostream> #include <string> using namespace std; string getString(); char * copyString(string s); int main() { string s = getString(); char* foo = copyString(s); for (int i = 0; i cout << foo[i]; } cout << endl; delete[] foo; < s.length(); i++) { return 0; } string getString() { string s; cout << "Please type a string: "; getline(cin,s); return s; } char* copyString(string s) { char* copy = new char[s.length()]; for (int i = 0; i < s.length(); i++) { copy[i]=s[i]; } return copy; } 5.2 Write a program to read test scores from a file and then stores in an array or similar data structure. You should do two versions of this program: one that uses a standard array, and another that uses a structure (such as an ArrayList or Vector) that can grow as needed to fit the data. You can assume that your file name is a constant value provided in the program. How do these versions differ? How can you ensure that you have enough space in the array-based version? Answer: Java: Version 1: import java.util.Scanner; import java.io.*; public class ReadScores { public static void main(String[] args) throws FileNotFoundException { String s = “grades”; FileReader reader = new FileReader(s); Scanner inFile = new Scanner(reader); // open file, read # of lines, close it int gradeCount = countGrades(inFile); inFile.close(); // re open reader = new FileReader(s); inFile = new Scanner(reader); int[] grades = readGrades(inFile,gradeCount); inFile.close(); for (int i = 0; i < grades.length; i++) { System.out.println(grades[i]); } } public static int countGrades(Scanner scan) { int cnt =0; while (scan.hasNextInt() == true) { cnt++; int s = scan.nextInt(); } return cnt; } public static int[] readGrades(Scanner in,int vals) { int[] grades = new int[vals]; int i =0; while (i < vals && in.hasNext()) { grades[i] = in.nextInt(); i++; } return grades; } } Version 2: import java.util.Scanner; import java.util.ArrayList; import java.io.*; public class ReadScores2 { public static void main(String[] args) throws FileNotFoundException { String s = “grades”; FileReader reader = new FileReader(s); Scanner inFile = new Scanner(reader); ArrayList<Integer> grades = readGrades(inFile); inFile.close(); for (int i = 0; i < grades.size(); i++) { System.out.println(grades.get(i)); } } public static ArrayList<Integer> readGrades(Scanner in) { ArrayList<Integer> grades = new ArrayList<Integer>(); while (in.hasNext()) { int grade = in.nextInt(); grades.add(grade); } return grades; } } C++: Version 1: #include <iostream> #include <fstream> using namespace std; int countGrades(ifstream& ); int *readGrades(ifstream& ,int); int main() { ifstream inp; inp.open("grades"); if (inp.fail() == true) { cout << "Can't open file"; exit(0); } int gradeCount = countGrades(inp); inp.close(); ifstream inp2; inp2.open("grades"); if (inp2.fail() == true) { cout << "Can't open file"; exit(0); } int* grades = readGrades(inp2,gradeCount); inp2.close(); for (int i = 0; i < gradeCount; i++) { cout << grades[i] << endl; } delete[] grades; return 0; } int countGrades(ifstream& inp) { int cnt =0; int x; while (inp.eof() == false) { cnt++; inp >> x; } return cnt; } int *readGrades(ifstream& inp,int vals) { int* grades = new int[vals]; int i =0; int x; while (i < vals && inp.eof() == false) { inp >> x; grades[i] = x; i++; } return grades; } Version 2: #include <iostream> #include <vector> #include <fstream> using namespace std; vector<int> readGrades(ifstream&); int main() { ifstream inp; inp.open("grades"); if (inp.fail() == true) { cout << "Can't open file"; exit(0); } vector<int> grades inp.close(); = readGrades(inp); for (int i = 0; i < grades.size(); i++) { cout << grades[i] << endl; } } vector<int> readGrades(ifstream& inp) { vector<int> grades; int x; while (inp.eof() == false) { inp >> x; grades.push_back(x); } return grades; } The array-based version must read the file twice, once to determine the number of integers, and the second time to read them. The number of integers is required to allocate the space needed before any reading begins. Since vectors/ArrayLists can grow as needed, their sizes do not need to be determined in advance. Thus, the second version does not need to read the file to determine the number of integers. The result is a significantly simpler file. Objective 6: Revise a program, eliminating vulnerabilities (CS2) Any of the programs given as exercises for assessing objective 3 can be used for this objective. Relevant strategies include catching exceptions to account for assignments of string to integers and range-checking integers before using them as array bounds.