0

Executing a shell command could be done by calling the system function which executes a shell command.

This is obviously not safe as someone can abuse the program and execute attacker-specific os commands.

The the man page of system suggests using the exec(3) function:

Do not use system() from a privileged program (a set-user-ID or set-group-ID program, or a program with capabilities) because strange values for some environment variables might be used to subvert system integrity. For example, PATH could be manipulated so that an arbitrary program is executed with privilege. Use the exec(3) family of functions instead, but not execlp(3) or execvp(3) (which also use the PATH environment variable to search for an executable).

Although I don't really understand the difference. Yes, the privileges might work differently, but we still executing os commands and it is still vulnerable, isn't it ? am I missing something ?

How can one execute a system command safely without worrying it will be abused ?

Wahalez
  • 489
  • 1
  • 6
  • 22
  • 1
    Option 1: you can't. Option 2: You know where the program is supposed to be installed, and specify the full pathname of the program securely in your call to `execv()` or `execve()`. If you don't know where the program is supposed to be installed, you are probably stuck. – Jonathan Leffler Aug 04 '21 at 13:29
  • What if I restrict special characters from the input ? will it fully cover the possible injections an attacker can do ? – Wahalez Aug 04 '21 at 13:49
  • 1
    — which special characters? Unless you restrict the valid characters to a restrictive list of allowed characters (e.g. unaccented Latin alphabet, digits, underscore and maybe dot and slash (but probably excluding dash), you are likely to run into problems sooner or later. Even so, there may be tricks an attacker can pull to screw things up. It depends in part on how much control the attacker has over the environment (including environment variables in particular) in which the program is run. – Jonathan Leffler Aug 04 '21 at 18:45

1 Answers1

2

we still executing os commands and it is still vulnerable, isn't it ?

Imagine the following C program (ignoring needed C error handling for clarity):

char file[20];
fgets(file, sizeof(file), stdin);
file[strcspn(file, "\n")] = 0; // https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input
char cmd[200];
snprintf(cmd, sizeof(cmd), "touch %s", file);
system(cmd);

All fine - a simple program to create a file. But now I run the program and type:

file; sudo rm -r /

So what would happen is that file="file; sudo rm -r /" then cmd="touch file; sudo rm -r /" and as a result system command runs touch file and then runs sudo rm -r / removing all your files and causing catastrophic damage.

In contrast:

execl("touch", "touch", file, NULL);

will always only just run touch (in the current directory) and touch will only try to touch a directory (because of the trailing /) named literally file; sudo rm -rf /. (But PATH can be manipulated and touch executable could be replaced, which is why use execv instead of execl - your quote tells that).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 2
    And it will fail unless the directory ```file; sudo rm -rf ```, complete with trailing blank, exists — because of the trailing slash. Also, `execl("touch", "touch", file, (char *)0);` provides the command name and the file name — and only executes `./touch`. You might need `execl("/usr/bin/touch", "touch", file, (char *)0);` to get things executed correctly. – Jonathan Leffler Aug 04 '21 at 13:31
  • So always use execv ? – Wahalez Aug 04 '21 at 13:33
  • 1
    @Wahalez: generally, `execv()` or `execve()` are the sensible choices. The `execl*()` functions require an argument list where the number of arguments is fixed at compile time, whereas the `execv*()` functions work with an array of string arguments terminated by a null pointer, just like `argv` provided to `main()`. Most often, you need that extra flexibility — using `execv(argv[0], argv)` or a similar call. Sometimes, and security-related programs could be one of those times, you do not want the flexibility and maybe using the `execl*()` functions is appropriate. – Jonathan Leffler Aug 04 '21 at 13:37
  • @KamilCuk I understood everything but this : `and touch will only try to touch a directory (because of the trailing /) named literally file; sudo rm -rf /.` why a directory and not a file for example ? because of the / in the string ? – Wahalez Aug 04 '21 at 14:14
  • 2
    `because of the / in the string ?` Yes `touch dir/` - then `dir` has to be a dir. – KamilCuk Aug 04 '21 at 14:39