可以在运行时更改 argv(不是由应用程序本身)

Анд*_*нко 7 c c++ security argv tocttou

我想知道可以main()在运行时更改输入参数。换句话说,在处理数据时,我们是否应该保护应用程序免受可能的TOCTTOU攻击argv?目前,我不知道有什么方法可以更改传入的数据argv,但我不确定这种方法不存在。

UPD:我忘了指出,我很好奇从程序外部更改 argv,因为 argv 是从程序外部接受的。

Mar*_*lli 11

我想说,根据您的威胁模型,这里有两个主要选项:

  1. 您不信任环境并假设您机器上的其他特权进程能够在程序运行时更改程序的内存内容。如果是这样,没有什么是安全的,程序可以被修改为几乎可以做任何事情。在这种情况下,您甚至不能相信整数比较。

  2. 您信任程序运行的环境。在这种情况下,您的程序是其数据的唯一所有者,只要您不明确决定更改argv或任何其他数据,您就可以依赖它。

在第一种情况下,您是否防范潜在的argv修改并不重要,因为您不信任执行环境,因此即使是那些警卫也可能被愚弄。在第二种情况下,您信任执行环境,因此您首先不需要防范问题。

在上述两种情况下,答案都是:,在处理argv.

TOCTTOU类型的问题通常来自外部不可信数据,这些数据可以被其他人修改,并且根据定义不应该被信任。一个简单的例子是文件的存在:你不能依赖它,因为机器上的其他用户或程序可以删除或移动它,确保文件可以被使用的唯一方法是尝试打开它。在 的情况下argv,数据不是外部的,而是由进程本身拥有的,因此该问题确实不适用。


Lui*_*ado 5

main()一般来说,传递到数组中的字符串集argv是在程序用户空间内设置的,大部分是在程序堆栈顶部的固定位置。

这种固定位置的原因是,随着程序ps在运行时的演变,某些程序会修改此区域,以允许特权程序(例如命令)收集并向您显示不同的命令参数。这用于类似程序sendmail(8)或用户程序的线程中,以显示哪个线程正在程序中执行什么工作。

这是一个非标准的功能,不同的操作系统使用不同的方式(我已经用 BSD 方式向您描述了)据我所知,linux 和 Solaris 也表现出这种行为。

一般来说,这使得 main 的参数属于用户进程空间,必须小心修改(使用某些操作系统特定的契约),因为它通常受到严格的约定的约束。该ps(1)命令挖掘它将要显示的进程的用户空间,以便显示显示命令参数的长列表。不同的操作系统文档(也许您可以从系统中使用的链接器标准脚本中获取确切的格式或调用系列如何初始化堆栈exec(2)-exec(2)手册页也应该有帮助)

我不完全知道这是否是您所期望的,或者您只是想看看是否可以修改参数......作为属于进程的用户空间的东西,它们很可能是可修改的,但我不能除了这个答案中描述的之外,猜猜这样做的任何理由。

顺便说一下,execlp(2)系统调用的 FreeBSD 手册页显示了以下摘录:

、、、 和的argvenvp参数的类型是一个历史意外,任何理智的实现都不应修改提供的字符串。虚假参数类型会触发正确性分析器的误报。在 FreeBSD 上,可以使用宏来解决此限制。execle()exect()execv()execvp()execvP()const__DECONST()

这清楚地表明您不能修改它们(至少在 FreeBSD 中)。我假设该ps(8)命令将以正确的方式处理验证这些参数的额外工作,以便永远不会出现安全问题错误(嗯,这可以进行测试,但我将其作为感兴趣的人的练习)

编辑

如果您查看/usr/include/sys/exec.hFreeBSD 中的(第 43 行),您会发现struct ps_strings位于用户堆栈顶部的一个命令用于ps(1)查找和定位进程环境和argv字符串。虽然您可以编辑它来更改程序提供的信息ps(1),但您有一个setproctitle(3)库函数(同样,所有这些都是 FreeBSDish,您必须深入了解 linux 或其他解决此问题的方式)

我已经尝试过这种方法,但它不起作用。今天有一个库函数调用来获得这种方法,但堆栈顶部实际上填充了上面提到的数据(我假设是出于兼容性原因)