Lab 2, CPSC 35200:Computer Security

Insecure Programs

Login to cs.hiram.edu using a terminal window and ssh cs.hiram.edu. In this lab we will investigate several programs and look at what vulnerabilities they have. We won’t use the virtual machine for this lab.

Background information: Study the file easy-guide-to-gcc.doc which documents some of the main features of the C compiler, gcc, and the debugger, gdb. Study the file program-layouts.doc on the web site which provides a rough guide to a C program compiled with gcc.

You may use any text editor that you wish. The file easy-guide-to-vim.doc surveys one text editor that you might want to use as it is simple, If you know emacs, you can use it.

Each of the programs below are very short and, consequently, it is fairly easy to spot some of the problems with the code. But, imagine that the code is part of a huge program, say several million or more lines of code! In that context finding vulnerabilities may not be easy.

We want to investigate some exploits. Using your text editor, create the files below that contain C code. (Note how the code is similar to Java without classes.) Try to keep the lines as shown here so when we go over these, we will all be looking at the same line, if, for example, we may wish to refer to line 3.

I suggest entering one file and then dong the exercises for that file before moving on.

//ex1.c

int main() {

int array[5] = {1,2,3,4,5};

printf("%d\n", array[5]); }

Note on C: The "%d\n" in the printf command is a control string. In C this means to take the variable array[5] and output it as an integer (%d) followed by a line feed (\n).

//ex2.c

int main () {

int array[5]={1,2,3,4,5};

int i;

for (i=0; i <= 4; ++i)

array[i] = 41; }

//ex3.c

void printInput() {

char buffer[16] = "aaaaaaaaaaaaaaa"; /* 15 a's */

gets(buffer);

printf("%s\n", buffer);

}

int main() {

printInput();

return 0; }

Note on C: The "%s\n" in the printf command means to take the variable array buffer and output it as a sting (%s). All strings are terminated with a null character.

//ex4.c

#include <stdio.h>

int IsPasswordOkay() {

char Password[12];

//gets(Password);

scanf("%s", Password);

//Assume "goodpass" was fetched from the password

//database.

if(!strcmp(Password, "goodpass"))

return (1);

else return(0); }

int main(void) {

int PwStatus;

puts("Enter password: ");

PwStatus = IsPasswordOkay();

if (PwStatus == 0)

puts("Access denied");

else puts("Access granted"; }

//ex5.c

#include <stdio.h>

int main() {

int a, b=5, c=6;

printf("Input an integer ");

scanf("%d",&a);

printf("%d\n",a);

printf("%d\n",b);

printf("%d\n",c); }

//ex6.c

int main() {

char str[8];

gets(str);

printf("%s\n",str); }

//ex7.c

#include <stdio.h>

#include <limits.h>

int main() {

int i;

unsigned int j;

printf("\n\n");

printf("add 1 to INT_MAX i\n");

i = INT_MAX;

printf("i= %d\n",i);

i++;

printf("i= %d\n",i);

printf("\n\n");

printf("add 1 to UINT_MAX j\n" );

j=UINT_MAX;

printf("j= %u\n",j);

j++;

printf("j= %u\n",j);

printf("\n\n");

printf("Subtract 1 from INT_MIN i\n");

i=INT_MIN;

printf("i= %d\n",i);

i--;

printf("i= %d\n",i);

printf("\n\n");

printf("subtract 1 from j = 0, j unsigned integer\n");

j=0;

j--;

printf("j= %u\n",j);

printf("\n\n"); }

//ex8.c

int main(int argc, char *argv[]){

int valid = 0; /* 0=false */

char str1[4];

char str2[4];

gets(str1);

gets(str2);

if (strncmp(str1,str2,4) == 0)

valid = 1; /* 1 = true */

printf("buffer1:str1(%s),str2(%s), valid(%d)\n",

str1, str2,valid);

if (valid == 0) /* zero is viewed as false */

printf("No match - deny access\n");

if (valid != 0) /*any nonzero value is viewed as true */

printf("Match - allow access\n"); }

I. Investigate ex1.c and variants of it

1-1) Do you see anything wrong with the ex1.c code? Compile and run it. What happens? Try to explain the output.

1-2) Let's try to either verify your explanation or to sharpen your answer.

Recompile ex1.c so you can use debugging information to internally look at the compiled code. Run the program under the debugger; set a breakpoint at line 1. Run the program and issue next commands until you see

printf("...

which means you are past the point where the variable array was initialized.

Examine 16 bytes of array.

What is the content of the word past the word holding 5, the value of array[4]? Is this value equal to the output you saw when ex1.c was run? Verify your answer by showing your work (Hint: Recall from cpsc 171 how to convert a hex number to binary and then recall that knowing the powers of 2 you can easily convert a binary number to a decimal number.)

1-3) Make a copy of ex1.c as we will modify the code and you may need to go back later and look at ex1.c. Refer to the new version as ex1a.c. Replace the printf line with

int i;

for (i=0; i <= 10; ++i)

printf("%d\n", array[i]);

What happens when you compile and run the code? Do you see any security vulnerability in this code? What happens if you make the upper bound of the loop very large?

1-4) It would be good to make a copy of ex1.c as we will modify the code again and you may need to go back later and look at earlier versions. I'll refer to the new version as ex1b.c. Change the code to the following; run it and observe what happens.

//ex1b.c

int main() {

char array[5] = "aaaa";

int i;

for (i=0; i<=4; i++)

printf("%c\n",array[i]);

}

The blank that comes out at the end is the null character as a string is always followed by a null character in C.

Now change ex1b.c so the upper bound on the for loop is 10 instead of 4. Run this version. What happens? Do you see any security vulnerabilities?

II. Investigate ex2.c and variants of it

2-1) Compile the code. What do you predict will happen? What did happen? Do you see any security problems?

2-2) Create ex2a.c using 10 as the upper bound for the loop. Compile the code. What do you predict will happen? What did happen? Do you see any security problems?

2-3) Create ex2b.c using 20 as the upper bound for the loop. Compile the code. What do you predict will happen? What did happen? Do you see any security problems? What do you think accounts for the different behavior in 2-3 and 2-4?

2-4) Investigate ex2.c, ex2a.c, and ex2b.c as below:

Recompile so you can use the debugger for each case. Do the following for each program:

Start a run with the debugger. Set a breakpoint at line 6 (assuming you reproduced the code exactly – ie. the number of lines are exactly the same.) Run the code and when the program stops on line 6, issue the command

(gdb) x/25 array. Can you see what is happening? (Hint What is 41 in hex notation?). Explain the difference. Why do they behave differently? (The last question is hard without cpsc 252 or until you answer the next section of questions).

III. Investigate ex3.c and variants of it

3-1) Do you see anything wrong with the ex3.c code? Compile and run it. You will get a warning about the function gets, but many programmers either ignore the warning or even turn such warnings off. Notice there is no prompt asking for the input. On the first run, enter less than 32 characters of an upper case A. What happens?

Now rerun the code and enter more than 32 characters of an upper case A.

What happens?

Do you see a security problem with the program? Explain.

3-2) Compile ex3.c under the debugger and run it. Set breakpoints on lines 3 and 4 and run the program.

When the program stops at the first breakpoint, note that input hasn't been asked for yet. What is the decimal and hex value of the ASCII code for “A”? Identify the location of the initial values of buffer by examining memory with

(gdb) x/16 buffer

Observe that these initial bytes at least are in buffer which is a local variable in the function printInput. Therefore, those bytes are on the stack in the frame for printInput.

Disassemble the function main and locate the return address. In what word (1sr, 2nd, etc.) is the address located starting to count from the start of buffer?

Examine that address (ADD) with

(gdb) x ADD

and observe that it IS back in main. Look at the word right before the one you identified as this is also needed for the return address.

3-3) Continue running the program with next. You will need to enter input (still without a prompt). Enter six A's and continue until the next breakpoint. Examine memory with

(gdb) x/16 buffer

and describe exactly what you see. Has the return address been corrupted in any way? How many A's (or any other letter) do you think you would need minimally to enter to overwrite the return address enough to crash the program?

Test your conjecture by running the code until you corrupt the return address and make the code crash, but nothing beyond it.

Check the overwrite bounds using the debugger.

3-4) What do you see as security vulnerabilities that these exercises suggest? Note- there are several possibilities.

IV-VIII Investigate ex4.c – ex8.c and variants of them

Investigate ex4.c – ex8.c as we did for the other programs.

Hint for ex4.c:

4-1) Compile and run the program at least 3 times. 1-Enter data that will allow access. 2-Enter data that fails to allow access, but dies gratefully. 3- Make the program crash. Analyze what is happening internally. Use this code to investigate the various debugger commands and see how they work. The command h produces help.

Hint for ex8.c:

8-1) Compile and run the program. You will be entering two strings on different lines (again without a prompt). Do three runs:

run 1: Enter start

start

run 2: Enter badinput

badinput1

run 3: Enter: badinput

badinputbadinput

Is this operating the way you would predict it would? Explain. Do you see a security vulnerability? Do you see a way to correct the basic setup?

8-2) Investigate this program internally for each of the runs using techniques introduced earlier in this lab. Use your work to see what is happening.