5

This question talks about using the system command and passing variables. Here is an example it gives:

string cmd("curl -b cookie.txt -d test=");
cmd += line;
cmd += "  http://example.com";
system(cmd.c_str());

One of the comments mentions that if line was passed and contained foo & fire_nukes.exe & REM then it's quite possible something bad could happen.

PHP has a great function called escape_shell_args which can be used to escape parameters that are being passed to the program. Does C++ have a way to do that?

Community
  • 1
  • 1
cwd
  • 53,018
  • 53
  • 161
  • 198

5 Answers5

4

Never pass user input into a system call. Ever.

You can try to escape all the characters that you think are dangerous, but you will get it wrong.

Instead, go at it from the other direction: let the user provide any of a selection of inputs that you define, then do a switch and build your own system call from scratch.

Admittedly this gets a little tricky when the input to the command-line application to invoke is freeform but, well, that's what actual libraries are for. libcurl, anyone?

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • What if you need to pass the variables that were passed to your program to another program? Perhaps a file path? This is what I'm attempting to do. The reason I haven't done it yet is because I know it's not "safe" and so I'd like to figure out what a safe(er) way to do it would be. thanks. – cwd Dec 16 '11 at 19:45
  • 1
    You mean a call to system(3), not a syscall as in syscalls(2) – Basile Starynkevitch Dec 16 '11 at 21:13
3

The best way is not to use system() at all. Use fork() and exec() and friends. Here's an example:

#include <string>
#include <unistd.h>
#include <error.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <cstdlib>

int fork_curl(char *line)
{
    std::string linearg("line=");
    linearg += line;

    int pid = fork();
    if(pid != 0) {
        /* We're in the parent process, return the child's pid. */
        return pid;
    }
    /* Otherwise, we're in the child process, so let's exec curl. */

    execlp("curl", "-b", "cookie.txt", "-d", linearg.c_str());  
    /* exec is not supposed to return, so something
       must have gone wrong. */
    exit(100);
    /* Note: You should probably use an exit  status
       that doesn't collide with these used by curl, if possible. */
}

int main(int argc, char* argv[])
{
    int cpid = fork_curl("test");
    if(cpid == -1) {
        /* Failed to fork */
        error(1, errno, "Fork failed");
        return 1;
    }

    /* Optionally, wait for the child to exit and get
       the exit status. */
    int status;
    waitpid(cpid, &status, 0);
    if(! WIFEXITED(status)) {
        error(1, 0, "The child was killed or segfaulted or something\n");
    }

    status = WEXITSTATUS(status);
    /* Status now contains the exit status of the child process. */
}
Staven
  • 3,113
  • 16
  • 20
  • 1
    Can you provide a link to the documentation of those commands I should be using? Or possibly give an example of how to use those commands? – cwd Dec 16 '11 at 19:38
  • an example *would* be nice :) – Steve Brown Dec 17 '11 at 05:17
  • @SteveBrown I've added an example. – Staven Dec 17 '11 at 10:58
  • NOOOOOO! execl replaces the current process and fork() is really badly defined on some platforms. Really NOOO! – Stephen Apr 17 '14 at 13:52
  • However, it seems that if the command is a shell script (with #!/bin/bash and chmod 777) you still must escape the arguments, otherwise you get /bin/bash: -c: line 0: syntax error near unexpected token `(' – Danny Mar 13 '16 at 12:31
0

You can use environment variables to safely pass parameters to the comand executed by system(3)

setenv ("mypar1", "'$(rm -rf /)", 1);
setenv ("mypar2", "\"; rm -rf /", 1);
system ("echo mypar1=\"$mypar1\" mypar2=\"$mypar2\");

Neither of these evil codes will be executed, simply printed.

Lorinczy Zsigmond
  • 1,749
  • 1
  • 14
  • 21
0

You could try execl..

http://linux.about.com/library/cmd/blcmdl3_execvp.htm

#include <unistd.h> 

int execl(const char *path, const char *arg, ...); 
nazar554
  • 303
  • 2
  • 7
  • Maybe this is what I need. If I want pass the variables that were passed to my program to the `/bin/echo` command, how would I do that? (Pointless but let's use it as an example) – cwd Dec 16 '11 at 19:49
  • You should define what *variables* mean for you. Once you understood well in your head what they mean, you nearly solved the problem. – Basile Starynkevitch Dec 16 '11 at 21:31
0

See the man page listing all syscalls (system calls are not the system(3) function which forks a shell).

Read more about system calls and linux kernel, and the overall organization of operating systems

Back to your question, you could quote for the shell (just follow the shell escape rules to transform your strings; for example prefix any non-letter character with a backslash).

See also the man pages of fork, execve, etc.

Read a good book on advanced linux programming and the famous Advanced Unix Programming

Use the freedom of most of the software on a Linux distribution. For example, you could study the source code of a simple shell like sash.

And perhaps you just want to use curl. Then the best way is to code for the libcurl library (without any call to system(3))

Have fun learning all this!

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 1
    The problem is that even escaping every non-letter is not enough. What if a malicious user launches a setuid program with a modified PATH and/or IFS? – ninjalj Dec 16 '11 at 21:35
  • I agree, but at least it answer the question. My feeling is that the poster should not use `system` nor fork a `curl` process but learn to call the functions from `libcurl` – Basile Starynkevitch Dec 16 '11 at 21:40
  • And the rest of your answer is quite good. But, really, `system()` is really dangerous. – ninjalj Dec 16 '11 at 21:48
  • Yes, I agree that using `system` is the wrong way. And I do know it is dangerous. But it is what the original poster imagines he want. (My point is that he just have to learn about libcurl). – Basile Starynkevitch Dec 16 '11 at 21:54
  • Your first two paragraphs are not relevant to the question; calls to `system()` and system calls are very different things. – Keith Thompson Dec 16 '11 at 22:18
  • I know that. I was suggesting to avoid `system(3)` and use only system calls. Actually, the original poster just need to learn how to call `libcurl` and avoid all the mess.... – Basile Starynkevitch Dec 16 '11 at 22:23