Can argv be changed at runtime (not by the app itself)
Asked Answered
U

2

7

I wonder can input parameters of main() be changed at runtime. In other words, should we protect the app from possible TOCTTOU attack when handling data in argv? Currently, I don't know any way to change data that was passed in argv, but I'm not sure that such ways don't exist.

UPD: I forgot to point out that I'm curious about changing argv from outside the program since argv is accepted from outside the program.

Unexperienced answered 30/1, 2020 at 13:16 Comment(9)
The argv vector is part of the process space, so only other processes, that may write to this process' memory, could alter it (usually this is not possible or only for privileged processes)Uraninite
Anything that can change the contents of argv can also change any other data in your process space; so DLL intection, debuggers etc. The question simplifies to: what can change arbitrary data in my programs process space?Varien
There's no difference between the strings in the argument array, and other strings or fixed-size arrays in the program.Workout
@ctx, imho, the question tries to ask if the process itself is allowed to modify the argv parameter to main(). As it belongs to the process, it should be capable, depending if the argument strings are located in read-only or writable memoy.Yeast
FWIW I've had crashing-problems under Windows when my process tried to set the char-pointers in main()'s original argv[] array to point to different strings. I avoided them by making a separate newArgv[] array to modify and use instead. (modifying argv[] directly seemed to work fine under other OS's though)Personage
@LuisColorado Even the title says not by the app itself, so why do you still think this?Uraninite
@ctx why do I think what?Yeast
@LuisColorado That "the question tries to ask if the process itself is allowed to modify the argv parameter to main()"Uraninite
You can always modify your readwrite user space in a process. As the data is stored in the stack (in FreeBSD, and probably also in linux) you can modify it, indeed. There's no aparent reason to do that, but you can, of course. See my answer to this question.Yeast
N
11

I'd say there are two main options based on your threat model here:

  1. You do not trust the environment and assume that other privileged processes on your machine are able to alter the contents of memory of your program while it is running. If so, nothing is safe, the program could be altered to do literally anything. In such case, you can't even trust an integer comparison.

  2. You trust the environment in which your program is running. In this case your program is the only owner of its data, and as long as you don't explicitly decide to alter argv or any other piece of data, you can rely on it.

In the first case, it doesn't matter if you guard against potential argv modifications, since you are not trusting the execution environment, so even those guards could be fooled. In the second case, you trust the execution environment, so you don't need to guard against the problem in the first place.

In both the above cases, the answer is: no, you shouldn't protect the app from a possible TOCTTOU attack when handling data in argv.

TOCTTOU kind of problems usually arise from external untrusted data, that can be modified by somebody else and should not be trusted by definition. A simple example is the existence of a file: you cannot rely on it, as other users or programs on the machine could delete or move it, the only way you can make sure the file can be used is by trying to open it. In the case of argv, the data is not external and is owned by the process itself, so the problem really does not apply.

Nightrider answered 30/1, 2020 at 13:27 Comment(10)
The answer contradicts the first sentence: You effectively argue, that it does not depend on the threat modelUraninite
@Uraninite the two points are examples of two different threat models.Nightrider
But it comes to the same conclusion, it seems?Uraninite
@Uraninite I don't really see how "you can't even trust an integer comparison" and "you can rely on it" are the same conclusion.Nightrider
The question is should we protect the app from possible TOCTOU attack when handling data in argv.. Do the two points answer this differently? I would say, that both points come to the conclusion: "no"Uraninite
@Uraninite the two points don't answer. The answer only depends on what OP decides to trust or not.Nightrider
Ok, if it does not try to answer the question, I can of course correct my voteUraninite
@Uraninite you're welcome to do so. I gave a clear answer, that is: depends on what your threat model is. The real answer varies from individual to individual, and is subject to OP's decisions to trust the system or not. There is no universally true answer to the question.Nightrider
And this is where I disagree. Both points lead to the answer "no". In the first case, anything can happen anyway, independently from argv-protecting, however this should take place. In the second case, it is safe anyway. Hence, the answer is "no" independently of the threat modelUraninite
Let us continue this discussion in chat.Nightrider
Y
5

In general, the set of strings that are passed to main() in the argv array are set inside the program user space, mostly in a fixed place at the top of the program stack.

The reason for such a fixed place, is that some programs modify this area to allow for a privileged program (e.g. the ps command) to gather and show you different command arguments, as the program evolves at runtime. This is used in programs like sendmail(8) or in user program's threads, to show you which thread is doing what job in your program.

This is a feature that is not standard, it is used differently by the different operating systems (I have described you the BSD way) As far as I know, linux also exhibits this behaviour and Solaris.

In general, this makes the arguments to main something that, belonging to the user process space, has to be modified with care (using some operating system specific contract), as it is normally subject to rigid conventions. The ps(1) command digs in the user space of the process it is going to show in order to show the long listing showing the command parameters. The different operating systems document (probably you can get this from the linker standard script used in your system the exact format or how the stack is intialized by the exec(2) familiy of calls -- the exec(2) manual page should be of help also)

I don't exactly know if this is what you expect, or if you just want to see if you can modify the arguments.... as something belonging to the user space of the proces, they are modifiable most probably, but I cannot guess any reasons to do that, apart of those described in this answer.

By the way, the FreeBSD manual page for the execlp(2) system call shows the following excerpt:

The type of the argv and envp parameters to execle(), exect(), execv(), execvp(), and execvP() is a historical accident and no sane implementation should modify the provided strings. The bogus parameter types trigger false positives from const correctness analyzers. On FreeBSD, the __DECONST() macro may be used to work around this limitation.

This states clearly that you cannot modify them (in FreeBSD at least). I assume the ps(8) command will handle the extra work of verifying those parameters in a proper way in order to never incurr in a security issue bug (well, this can be tested, but I leave it as an exercise for the interested people)

EDIT

If you look at /usr/include/sys/exec.h (line 43) in FreeBSD, you will find that there's a struct ps_strings located in the top of the user stack, that is used by ps(1) command to find and locate the the process environment and argv strings. While you can edit this to change the information a program gives to ps(1), you have a setproctitle(3) library function (again, all of this is FreeBSDish, you'll have to dig to get the way linux, or other, solves this problem)

I've tried this approach, but it doesn't work. Today there's a library function call to get this approach, but the top of the stack is actually filled with the data mentioned above (I assume for compatibility reasons)

Yeast answered 31/1, 2020 at 4:11 Comment(2)
The argv vector is common to all threads, so you can't modify it individually for one thread.Uraninite
There are multithread applications that allow you to show different arg lists for each thread in ps(8) output. On these, you need a pointer per thread (it is located on the thread stack at the top) so it can be unique per thread. In FreeBSD at least, the argc, argv pointers vector and the strings it points to, are located at the top of the stack, and there's a pointer also there for you to modify if you want to show ps(8) changed argument lists. See mckusikc's book on the designt of FreeBSD and you'll find this information.Yeast

© 2022 - 2024 — McMap. All rights reserved.