【问题标题】:Can argv be changed at runtime (not by the app itself)可以在运行时更改 argv(而不是由应用程序本身)
【发布时间】:2020-08-12 19:13:43
【问题描述】:

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

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

【问题讨论】:

  • argv 向量是进程空间的一部分,因此只有可能写入该进程内存的其他进程才能更改它(通常这是不可能的或仅适用于特权进程)
  • 任何可以改变argv内容的东西也可以改变你进程空间中的任何其他数据;所以DLL intection,调试器等。问题简化为:什么可以改变我的程序进程空间中的任意数据?
  • 参数数组中的字符串与程序中的其他字符串或定长数组没有区别。
  • @ctx,恕我直言,问题试图询问是否允许进程本身将argv参数修改为main()。由于它属于进程,它应该能够,具体取决于参数字符串是位于只读存储器还是可写存储器中。
  • FWIW 当我的进程尝试将 main() 的原始 argv[] 数组中的字符指针设置为指向不同的字符串时,我在 Windows 下遇到了崩溃问题。我通过创建一个单独的 newArgv[] 数组来修改和使用来避免它们。 (虽然在其他操作系统下直接修改 argv[] 似乎可以正常工作)

标签: c++ c security argv tocttou


【解决方案1】:

根据您的threat model,我想说有两个主要选项:

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

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

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

在上述两种情况下,答案都是:,在处理argv 中的数据时,您不应该保护应用免受可能的 TOCTTOU 攻击。

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

【讨论】:

  • 答案与第一句话相矛盾:你有效地争辩说,它确实取决于威胁模型
  • @Ctx 这两点是两种不同威胁模型的示例。
  • 但似乎得出了相同的结论?
  • @Ctx 我真的不明白 “你甚至不能相信整数比较”“你可以依赖它”是相同的结论。
  • @Ctx 欢迎您这样做。我给出了明确的答案,那就是:取决于你的威胁模型是什么。 真正的答案因人而异,并且取决于 OP 是否信任系统的决定。这个问题没有普遍正确的答案。
【解决方案2】:

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

这样一个固定位置的原因是,一些程序修改了这个区域以允许特权程序(例如ps 命令)随着程序在运行时的发展而收集并显示不同的命令参数。这用于sendmail(8) 之类的程序或用户程序的线程中,以显示哪个线程在您的程序中执行什么工作。

这是一个不标准的特性,不同的操作系统使用它的方式不同(我已经向你描述了 BSD 方式)据我所知,linux 和 Solaris 也有这种行为。

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

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

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

argvenvp参数的类型为execle(),exect(),execv(), execvp()execvP() 是历史性事故,不理智 实现应该修改提供的字符串。虚假参数 类型触发来自const 正确性分析器的误报。上 FreeBSD,__DECONST() 宏可以用来解决这个问题 限制。

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

编辑

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

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

【讨论】:

  • argv向量对所有线程都是通用的,所以不能为一个线程单独修改。
  • 有多线程应用程序允许您在ps(8) 输出中为每个线程显示不同的参数列表。在这些上,每个线程都需要一个指针(它位于顶部的线程堆栈上),因此每个线程它可以是唯一的。至少在 FreeBSD 中,argcargv 指针向量和它指向的字符串位于堆栈的顶部,如果你想显示 ps(8) 更改,还有一个指针供你修改参数列表。请参阅 mckusikc 关于 FreeBSD 设计的书,您会找到这些信息。
猜你喜欢
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
  • 2021-10-27
  • 1970-01-01
  • 1970-01-01
  • 2022-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多