Are ALL system() calls a security risk in c++? - c++

A post in this (Are system() calls evil?) thread says:
Your program's privileges are inherited by its spawned programs. If your application ever runs as a privileged user, all someone has to do is put their own program with the name of the thing you shell out too, and then can execute arbitrary code (this implies you should never run a program that uses system as root or setuid root).
But system("PAUSE") and system("CLS") shell to the OS, so how could a hacker possibly intervene if it ONLY shells to a specific secure location on the hard-drive?
Does explicitly flush—by using fflush or _flushall—or closing any stream before calling system eliminate all risk?

The system function passes command to the command interpreter, which executes the string as an operating-system command. system uses the COMSPEC and PATH environment variables to locate the command-interpreter file CMD.exe. If command is NULL, the function just checks whether the command interpreter exists.
You must explicitly flush—by using fflush or _flushall—or close any stream before you call system.
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/system-wsystem
In case, there are any doubts here's the actual snippet from the MS' implementation (very simple and straightforward):
// omitted for brevity
argv[1] = _T("/c");
argv[2] = (_TSCHAR *) command;
argv[3] = NULL;
/* If there is a COMSPEC defined, try spawning the shell */
/* Do not try to spawn the null string */
if (argv[0])
{
// calls spawnve on value of COMSPEC vairable, if present
// omitted for brevity
}
/* No COMSPEC so set argv[0] to what COMSPEC should be. */
argv[0] = _T("cmd.exe");
/* Let the _spawnvpe routine do the path search and spawn. */
retval = (int)_tspawnvpe(_P_WAIT,argv[0],argv,NULL);
// clean-up part omitted
As to concerns of what _tspawnvpe may actually be doing, the answer is: nothing magical. The exact invocation sequence for spawnvpe and friends goes as following (as anybody with licensed version of MSVC can easily learn by inspecting the spanwnvpe.c source file):
Do some sanity checks on parameters
Try to invoke _tspawnve on the passed file name. spawnve will succeed if the file name represents an absolute path to an executable or a valid path relative to the current working directory. No further checks are done - so yes, if a file named cmd.exe exists in current directory it will be invoked first in the context of system() call discussed.
In a loop: obtain the next path element using `_getpath()
Append the file name to the path element
Pass the resulted path to spwanvpe, check if it was successful
That's it. No special tricks/checks involved.

The original question references POSIX not windows. Here there is no COMSPEC (there is SHELL but system() deliberately does not use it); however /bin/sh is completely, utterly vulnerable.
Suppose /opt/vuln/program does system("/bin/ls"); Looks completely harmless, right? Nope!
$ PATH=. IFS='/ ' /opt/vuln/program
This runs the program called bin in the current directory. Oops. Defending against this kind of thing is so difficult it should be left to the extreme experts, like the guys who wrote sudo. Sanitizing environment is extremely hard.
So you might be thinking what is that system() api for. I don't actually know why it was created, but if you wanted to do a feature like ftp has where !command is executed locally in the shell you could do ... else if (terminalline[0] == '!') system(terminalline+1); else ... Since it's going to be completely insecure anyway there's no point in making it secure. Of course a truly modern use case wouldn't do it that way because system() doesn't look at $SHELL but oh well.

Related

C++ application: clear commandline arguments?

I have a C++ application which uses CCommandLineInfo to parse command line arguments.
One of these arguments is a password which we encrypt in memory with CryptProtectMemory after the application starts.
At that point I want to get rid of the password which is still in plain text available in memory (when I create a memory dump it can be retrieved).
Is there a way to clear the command line arguments? I tried clearing (overwriting with empty strings) __argv but the arguments were still visible in the memory dump.
[edit]
I tried clearing the commandline arguments like this, but that didn't work.
The arguments are still in memory.
for (int i=0; i<__argc; i++)
__argv[i] = "----------------------";
TCHAR* cmdLine = GetCommandLine();
SecureZeroMemory(cmdLine, strlen(cmdLine));
There is a well-known trick/hack to clear the command line from the process memory (see this answer), but even if you apply it you can still easily fetch the command line from e.g. Process Explorer since it makes a copy of it when the process is started. Thus, there is no way to prevent a tool like this from showing the command line.
Having a password as a command line parameter is simply a no-no. The only solution I can think of is to store the password encrypted/hashed (or worst case; unencrypted) in a file and then load that file as a parameter.
I'm afraid cleaning up argv is not enough, as the source of argv is still available using GetCommandLine(). Ultimately this information is stored in RTL_USER_PROCESS_PARAMETERS in Process Environment Block. C runtime will cache this information to argv. Some other library may cache this information too.
You'd better pass your sensitive data with other IPC - shared memory or pipe. Then you need to clean only your memory.
If you still want to locate original command line, here's approximate direction: NtCurrentTeb() to get TEB, then there would be pointer to PEB, and there would be pointer to RTL_USER_PROCESS_PARAMETERS, which finally contains pointer to command-line.

Turbo C++ system function running an executable

How to run any exe file from turbo c++? I know that I should stop using turbo c++ and move one to Dev or Code::Blocks, but my school doesn't agree so I gotta wing it.
I just want to know how to run a file with or without the system() function.
Any kind of advice is welcome
Here's what I have tried so far:
1
#include<process.h>
int main()
{
system("tnfsv13.exe"); //tnfsv being a 16-bit application(The need for slowness v 13)
return 0;
}
2
#include<process.h>
int main()
{
system("tnfsv13.bat");
return 0;
}
tnfsv13.bat:
start "c:\TurboC3\BIN\" tnfsv13.exe
NOTE: Just a doubt, you guys: system() is not working in windows XP. I tried it using dosbox in windows 7 and it works well, but in XP it does absolutely nothing. Not even the system("dir") command seems to work but system(NULL) returns 1. Any guesses why?
Thanks.
You can also use Turbo C++'s execl() function. execl() loads and runs C:\\TC\\BIN\\tnfsv13.exe. NULL means there are no arguments to send to tnfsv13.exe. If an error occurs, execl() returns -1 into int c .
#include<stdio.h>
#include<process.h>
int main()
{
int c = execl("C:\\TC\\BIN\\tnfsv13.exe", NULL);
return 0;
}
Explanation:
execl() loads and executes a new child process. Because the child
process is placed in the memory currently occupied by the calling
process, there must be sufficient memory to load and execute it.
'pathname' specifies the file name of the child process. If
'pathname' has a file name extension, then only that file is searched
for. If 'pathname' ends with a period (.), then 'pathname' without an
extension is searched for. If that filename is not found, then
".EXE" is appended and execl() searches again. If 'pathname' has no
extension and does not end with a period, then execl() searches for
'pathname' and, if it is not found, appends ".COM" and searches
again. If that is not found, it appends ".EXE" and searches again.
'arg0', 'arg1',...'argn' are passed to the child process as command-
line parameters. A NULL pointer must follow 'argn' to terminate the
list of arguments. 'arg0' must not be NULL, and is usually set to
'pathname'.
The combined length of all the strings forming the argument list
passed to the child process must not exceed 128 bytes. This includes
"n" (for 0-n arguments) space characters (required to separate the
arguments) but does not include the null ('\0') terminating
character.
Returns: If execl() is successful, it does not return to the
calling process. (See the spawn...() routines for a
similar function that can return to the calling
process). If an error occurs, execl() returns -1 to
the calling process. On error, 'errno' (defined in
<errno.h>) is set to one of the following values
(defined in <errno.h>):
E2BIG Argument list or environment list too big.
(List > 128 bytes, or environment > 32k)
EACCES Locking or sharing violation on file.
(MS-DOS 3.0 and later)
EMFILE Too many files open.
ENOENT File or path not found.
ENOEXEC File not executable.
ENOMEM Not enough memory.
Notes: Any file open when an exec call is made remains open
in the child process. This includes
'stdin','stdout', 'stderr', 'stdaux', and 'stdprn'.
The child process acquires the environment of the
calling process.
execl() does not preserve the translation modes of
open files. Use setmode() in the child process to
set the desired translation modes.
See the spawn...() routines for similar though more
flexible functions that can return to the calling
program.
Caution: The file pointers to open buffered files are not
always preserved correctly. The information in the
buffer may be lost.
Signal settings are not preserved. They are reset to
the default in the child process.
-------------------------------- Example ---------------------------------
The following statements transfer execution to the child process
"child.exe" and pass it the three arguments "child", "arg1",
and"arg2":
#include <process.h> /* for 'execl' */
#include <stdio.h> /* for 'printf' and 'NULL' */
#include <errno.h> /* for 'errno', 'ENOENT' and 'ENOMEM' */
main()
{
execl("child.exe", "child", "arg1", "arg2", NULL);
/* only get here on an exec error */
if (errno == ENOENT)
printf("child.exe not found in current directory\n");
else if (errno == ENOMEM)
printf("not enough memory to execute child.exe\n");
else
printf(" error #%d trying to exec child.exe\n", errno);
}
system() works fine, though it may not work exactly the way you expect: it does the same thing as typing a command at a MSDOS (or Win32) command prompt including input and output being connected to the console.
If you just want to run a program, pass parameters, and not return from it, use a convenient form from the exec() family of functions. See this for one example.

how to JUDGE other program's result via cpp?

I've got a series of cpp source file and I want to write another program to JUDGE if they can run correctly (give input and compare their output with standart output) . so how to:
call/spawn another program, and give a file to be its standard input
limit the time and memory of the child process (maybe setrlimit thing? is there any examples?)
donot let the process to read/write any file
use a file to be its standard output
compare the output with the standard output.
I think the 2nd and 3rd are the core part of this prob. Is there any way to do this?
ps. system is Linux
To do this right, you probably want to spawn the child program with fork, not system.
This allows you to do a few things. First of all, you can set up some pipes to the parent process so the parent can supply the input to the child, and capture the output from the child to compare to the expected result.
Second, it will let you call seteuid (or one of its close relatives like setreuid) to set the child process to run under a (very) limited user account, to prevent it from writing to files. When fork returns in the parent, you'll want to call setrlimit to limit the child's CPU usage.
Just to be clear: rather than directing the child's output to a file, then comparing that to the expected output, I'd capture the child's output directly via a pipe to the parent. From there the parent can write the data to a file if desired, but can also compare the output directly to what's expected, without going through a file.
std::string command = "/bin/local/app < my_input.txt > my_output_file.txt 2> my_error_file.txt";
int rv = std::system( command.c_str() );
1) The system function from the STL allows you to execute a program (basically as if invoked from a shell). Note that this approach is inherenly insecure, so only use it in a trusted environment.
2) You will need to use threads to be able to achieve this. There are a number of thread libraries available for C++, but I cannot give you recommendation.
[After edit in OP's post]
3) This one is harder. You either have to write a wrapper that monitors read/write access to files or do some Linux/Unix privilege magic to prevent it from accessing files.
4) You can redirect the output of a program (that it thinks goes to the standard output) by adding > outFile.txt after the way you would normally invoke the program (see 1)) -- e.g. otherapp > out.txt
5) You could run diff on the saved file (from 3)) to the "golden standard"/expected output captured in another file. Or use some other method that better fits your need (for example you don't care about certain formatting as long as the "content" is there). -- This part is really dependent on your needs. diff does a basic comparing job well.

C++: How to escape user input for safe system calls?

On a Linux platform, I have C++ code that goes like this:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
std::string command;
command = "mkdir " + myDir;
if (system(command.c_str()) != 0) {
return 1;
}
// continue....
Is passing user input to a system() call safe at all?
Should the user input be escaped / sanitized?
How?
How could the above code be exploited for malicious purposes?
Thanks.
Just don't use system. Prefer execl.
execl ("/bin/mkdir", "mkdir", myDir, (char *)0);
That way, myDir is always passed as a single argument to mkdir, and the shell isn't involved. Note that you need to fork if you use this method.
But if this is not just an example, you should use the mkdir C function:
mkdir(myDir, someMode);
Using system() call with command line parameters without sanitizing the input can be highly insecure.
The potential security threat could be a user passing the following as directory name
somedir ; rm -rf /
To prevent this , use a mixture of the following
use getopt to ensure your input is
sanitized
sanitize the input
use execl instead of system to execute
the command
The best option would be to use all three
Further to Matthew's answer, don't spawn a shell process unless you absolutely need it. If you use a fork/execl combination, individual parameters will never be parsed so don't need to be escaped. Beware of null characters however which will still prematurely terminate the parameter (this is not a security problem in some cases).
I assume mkdir is just an example, as mkdir can trivially be called from C++ much more easily than these subprocess suggestions.
Reviving this ancient question as I ran into the same problem and the top answers, based on fork() + execl(), weren't working for me. (They create a separate process, whereas I wanted to use async to launch the command in a thread and have the system call stay in-process to share state more easily.) So I'll give an alternative solution.
It's not usually safe to pass user input as-is, especially if the utility is designed to be sudo'd; in order to sanitize it, instead of composing the string to be executed yourself, use environment variables, which the shell has built-in escape mechanisms for.
For your example:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
setenv("MY_DIR", myDir, 1);
if (system("mkdir \"${MY_DIR}\"") != 0) {
return 1;
}
// continue....

Executing a command from C++, What is expected in argv[0]?

I am using execv() to run commands from /bin/ such as 'ls', 'pwd', 'echo' from my c++ program, and I am wondering what value I should provide in argv[0];
const char * path = getPath();
char ** argv = getArgs();
execv(path,argv);
argv[0] is supposed to be the program name. It's passed to the program's main function. Some programs differentiate their behavior depending on what string argv[0] is. For example the GNU bash shell will disable some of its features if called using sh instead of bash. Best give it the same value that you pass to path.
In linux, argv[0] is the process name displayed by the top utility (which it probably gets from reading entries in /proc/)
argv[0] should be the full path of the command that you want to run.
I know that this is not the answer you're looking for but is there a specific reason why you're doing this? The reason I ask is that most if not all of the actions people normally run with either system() or execv() are available in libraries on either Windows or Unix and are safer, faster and less likely to suffer from circumstantial errors. By that I mean, for example, when the PATH changes and suddenly your code stops working.
If you're passing in a string, either in whole or in part, and running it then you also leave yourself open to a user gaining access to the system by entering a command that could be damaging. E.g. imagine you've implemented a file search using find /home -name and your user types in:
"%" -exec rm {} \;
Ouch!