Download Security Injection Assessments: Buffer Overflow Assessments

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
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.