* 9 *

Software security I

Self-test questions

  1. Describe the main themes one needs to address in designing secure software.
  2. Explain why the following system call, for reading input from the user is inherently dangerous.
    char buffer[1024];
    
    gets(buffer);
    
    Hint: what information or argument is the function gets() missing?

Graded problems

This week, the purpose of the practical work is to illustrate how buffer overflows can lead to security problems in carelessly written software. We shall be discussing this in the coming weeks. Compile the following program, first on Solaris (dax) and then on GNU/Linux (cube), e.g.
gcc -o program overflow1.c
to check whether the result depends on the operating system.
(download)
/*****************************************************************************/
/*                                                                           */
/* File: overflow1.c                                                         */
/*                                                                           */
/*****************************************************************************/

#include 

main()

{ char buffer[8];

printf("Please enter your name:"); 
scanf("%s",buffer);

printf("Your name is: [%s]\n",buffer);

Function1();
Function2(); 
}

/*****************************************************************************/

Function1()

{ char buffer[100];

sprintf (buffer,"Mary had a little lamb whose fleece was white as /bin/sh BadScript ...");
printf("%.22s\n",buffer); 
}

/*****************************************************************************/

Function2()

{ char buffer[50];
 
printf("Execute command %s\n",buffer);
}


Now answer these questions:
  1. Look at the buffer sizes of the character arrays in the different functions.
  2. Try typing in a very long name at the prompt. The output of the program is something like this:
    Please enter your name:abcdef
    Your name is: [abcdef]
    Mary had a little lamb
    Execute command  /bin/sh BadScript ...    
    
    Can you explain why?
  3. Suppose this had been a program running as root and that Function2 had not merely printed `executing command' but had really executed it! Also, suppose function 1 had collected a string from the user, instead of using a static string...what is the danger here? How could it have been avoided?

Now try the following program (download):
/*****************************************************************************/
/*                                                                           */
/* File: overflow2.c                                                         */
/*                                                                           */
/*****************************************************************************/

#include 

main()

{
Function1();
Function2(); 
}

/*****************************************************************************/

Function1()

{ char buffer[100];

scanf("%[^\n]",buffer);
}

/*****************************************************************************/

Function2()

{ char buffer[50];

printf("01234567890123456789001234567890012345678901234567890\n"); 
printf("Executing command %s\n",buffer); 
system (buffer);
}

One of the biggest problems for programmers who write interactive programs (including programs which interact over a network, such as client-server systems) is in handling streams. You have probably heard about streams in connection with the streams library for C++, but you should not think that this is only something to do with that library.

A stream of data is a continuous feed of input/output which contains multiple data items. The length of a data stream is unknown at the start of transmission, it's end is marked by an EOF (end of file) condition. Transfer of data by streams can be contrasted with the transfer of blocks of data, of known size.

The most well-known streams are called standard in/stdin and standard out/stdout . They are usually keyboard input and console output. Each time we ask the user to type in some data, we open a channel to the keyboard stream. Input ends when the user types ENTER.

The purpose of this week's exercise is to highlight the problems involved in stream programming. Why is this difficult? Why is this one of the most common sources of error in interactive programming? We shall be coding both in C and in C++.

  1. The complementary functions for reading in data in C and C++ are, respectively
    
          C                        C++
    
       int a;                   int a;
    
       scanf("%d",&a);          cin >> a;
    
    These are amongst the most difficult and dangerous functions in input processing, for two reasons: Try compiling the following program in C/C++:
    
     #include <stdio.h>                               #include <iostream>
    
     main()                                               main()
    
     { int a = 0;                                         { int a = 0;
       static char buffer[20] = "rm *";                     static char buffer[20] = "rm *"; 
    
     printf ("Enter [uid] [command]: ");                   cout << "Enter [uid] [command]: ";
    
     scanf("%d %s",&a,buffer);                             cin >> a >> buffer;
    
     printf("Okay, executing %s as user %d\n",buffer,a);   cout << "Okay, executing " << buffer << " as user " << a << endl;
     }                                                     }
    
    
    Try typing in the following input:
    100 ls
    100ls
    ls 100
    
    Can you explain the results? Suppose, this has been a server on a Unix host, what would be the result of the last line?

  2. Suppose we are writing a secure protocol for transferring orders from government to a nuclear submarine. Let us simulate the communications channel by using standard input, but the same thing applies to other communcations as we shall see in the next lecture. Here we shall look at incorrect interpretations and denial of service attacks. Type in this C++ program:
    #include <iostream>
    
    main()
    
    { char command[40];   // Send command to 
      int time_of_day;    // Avoid replay attack?
    
    cin >> time_of_day >> command;
    cout << "Command was " << command << " at time " << time_of_day << endl;
    }
    
    and try it with the following input:
    13 report
    13 shoot-to-kill
    15 shoot only if they shoot first
    12:00 fire
    

    Suppose now, we make this into a loop to receive new commands, like a server:

    #include <iostream>
    
    main()
    
    { char command[40];   // Send command to 
      int time_of_day;    // Avoid replay attack?
      const bool ever = 1;
      
    for ( ;ever; )
       {
       cin >> time_of_day >> command;
       cout << "Command was " << command << " at time " << time_of_day << endl;
       }
    }
    
    
    Try entering the same input now. Can you see how easy it is to perform a denial of service attack on this server? This same error was present in NT4, prior to service pack 2. This problem is difficult to fix with the C++ stream library, but easy to fix with C's I/O library:
    #include <stdio.h>
    
    #define ever 1
    
    main()
    
    { char command[40];   // Send command to 
      int time_of_day;    // Avoid replay attack?
      
    for ( ;ever; )
       {
       scanf("%d %[^\n]",&time_of_day,command);
       printf("Command %s at time %d\n",command,time_of_day);
       }
    }
    
    
    The regular expression matcher %[^\n] means `match any object consisting of any character up to end of line'.
ELECTRIC FENCE?? Document your work for the second project submission.

Back