* 10 *

Softwaresikkerhet II

Self test questions

  1. Suppose that you are writing a program and you need to construct the full name of a file which you will open. The filename consists of a directory-path and the actual nodename. Nodenames are usually restricted to being no more than 255 characters long. Explain at least two things wrong with the following:
    
    char *BuildName(char *path, char *name)
    
    { char buffer[256];
    
    sprintf(buffer,"%s/%s",path,name);
    
    return buffer;
    }
    
    
  2. Rewrite the function above so that it is secure. Hint, use strlen() to check lengths.
  3. Suppose the path were the name of a user, and we intend to write to the file e.g.
    name = BuildName("/home/mark",".cshrc");
    
    fp = fopen(name,"w");
    
    
    Describe an explain a safe way to open the file for a system process. Describe what can go wrong in this kind of simple operation.
  4. Explain what is meant by a race or race condition.
  5. Explain what is meant by a sandbox security model.
  6. Describe the measures taken by the Java language for applet security.

Karaktergivende oppgaver

The purpose of the practical work this week is demonstrate more software blunders.
  1. The first problem has to do with setting the correct permissions on files. Files do not always end up with the permissions we intend them to get. Inheritance of attributes can spoil this. Look up the following manual pages:
    man 2 chmod
    man stat
    
    Change your security kit program file appl.cpp so that it contains the following:
    main ()
    
    { FILE *fp;
      struct stat statbuf;
    
    umask(0);   // Change this and remove it...see what happens
    
    if ((fp = fopen("newfile","w")) == NULL)
       {
       perror("fopen");
       return error;
       }
    
    fprintf(fp,"This is an example file.");
    
    fclose(fp);
    
    // Now check permissions
    
    if (stat("newfile",&statbuf) == -1)
       {
       perror("stat");
       return error;
       }
    
    printf("The file perms on the file are %o\n",statbuf.st_mode & 07777);
    
    chmod("newfile",0755);
    
    if (stat("newfile",&statbuf) == -1)
       {
       perror("stat");
       return error;
       }
    
    printf("The file perms on the file are now %o\n",statbuf.st_mode & 07777);
    }
    
    
    Compile and run this program. Try deleting the umask line from the program and changing the value of umask in your shell instead:
    % umask 0
    % ./appl
    % umask 022
    % ./appl
    % umask 077
    % ./appl
    
    Remember to delete the file newfile in between, to see the effect of file creation. This inheritance of file attributes can cause problems!

  2. In the Security Kit, type in the following program. This program uses a pipe stream in order to execute a shell command (ls -al) and read the output from that command. It then echoes the output back to the screen.
    
    main()
    
    { FILE *pp;
      char line[1024];
    
    line[0] = '\0';
    
    if ((pp = popen("ls -al","r")) == NULL)
       {
       printf("cannot open\n");
       exit(0);
       }
    
    while(!feof(pp))
       {
       fgets(line,1024,pp);
       printf("comm: %s",line);
       }
    
    printf("Done with program\n");
    
    pclose(pp);
    }
    
    
    Notice how the program does not specify a path for the command ls, but the program compiles and runs anyway. The reason for this is that an implicit Bourne shell is used to interpret the command, and the user path includes a command ls.

    Now change

    popen()   ->  cfpopen()
    pclose()      cfpclose()
    
    in the program and note that the command is no longer executed. Instead a blank stream is opened, to the command which was not found. Although this is slightly dubious behaviour, it indicates that no shell is now involved in the attempt at execution of the command.

    Note: a path should always be specified. Check what happens if you link the command ls to your current working directory like this:

    ln -s /bin/ls ./ls
    
    Rerun the programs and verify that the exec functions which work behind the scenes for (cf)popen look for programs, which do not have explicit paths, in the current directory. This also provides scope for errors.
  3. The purpose of this exercise is to look at a simulation of race conditions in slow-motion. It is about how streams of data interact with shared resources, and how attackers can exploit the time between context switches/packet delivery in order to add something to the data stream.

    To complete this exercise, you will need three windows, one for running each process: a sender, a receiver and an attacker. You should compile the programs below. You do not need the Security Kit to do this, it should be enough to compile with

    gcc -o prog1 prog1.c
    
    and so on.

    Here is the sender program. It is an extremely simpified program which generates a stream of data. To make the order of things clear, it simply outputs a number of line numbers. These are sent into a file called pipe_simul, which is our representation of a pipe between the programs. For instance, it might be a Unix pipe (inter-process communication) or it might be a network connection between server and client.

    #include <stdio.h>
    
    main()
    
    { FILE *fp;
      int i;
      char line[1024];
    
    for (i = 0; i < 10; i++)
       {
       if ((fp = fopen("pipe_simul","a")) == NULL)
          {
          exit(1);
          }
    
       fprintf(fp,"Line %d\n",i);
    
       fclose(fp);
    
       sleep(5);  /* simulate context switch or delay between packets */
       }
    }
    
    Here is the receiver program. It reads the data stream from the pipe and outputs the data on the screen.
    #include <stdio.h>
    
    main()
    
    { FILE *fp;
      char line[1024];
    
    fp = fopen("pipe_simul","r");
    
    while (!feof(fp))
       {
       fgets(line,1023,fp);
    
       printf("read: %s\n",line);
    
       sleep(5);   /* Context switch */
       }
    
    fclose(fp);
    }
    
    Compile these programs and run them, one in each window. Start the first program first. You should see how the first program transmits data and how the second receives the data. Look through the programs and make sure that you understand how this happens.

    Now delete the file pipe_simul and start again. This time, when the processes are running, try typing the following into the third window:

    echo "Attack by a man in the middle" >> pipe_simul
    
    You should see that the data appear in the output of the second process, after a short time. If you like, you can increase the rate of transfer by reducing the sleep time. Do you see how this can be used to spoof a challenge-response system?

    The point is this: as long as the middle man process has access to the medium by which the data are transferred, it is easy for an attacker to try this kind of attack.. In the case of internet connections, every host has access to the internet. Of course, the attacker needs some prior knowledge of the type of transactions taking place. Also, it gets harder to make a successful attack as we turn up the transfer rate. Here it was trivial, in reality everything is much more time-critical.

    Your group should document these exercises for the second project submission.

    Back