“!xxx%s%s%s%s%s%s%s%s”有什么特别之处?

Ale*_*nov 4 history csh historical-unix

我链接到The Unix-Haters Handbook并偶然发现(第 149 页):

主题:相关的 Unix 错误

1991 年 10 月 11 日

W4115x 同学——

当我们讨论激活记录、参数传递和调用约定时,您是否知道键入:

!xxx%s%s%s%s%s%s%s%s
Run Code Online (Sandbox Code Playgroud)

任何 C-shell 都会导致它立即崩溃?你知道为什么吗?

需要思考的问题:

  • 当您键入时,shell 会做什么!xxx
  • 当您输入时,它必须对您的输入做什么 !xxx%s%s%s%s%s%s%s%s
  • 为什么这会导致外壳崩溃?
  • 你怎么能(相当容易地)重写 shell 的有问题的部分,以免出现这个问题?

纯粹出于好奇,谁能解释一下问题出在哪里?不出所料,在 Google 上搜索字符串并没有帮助。从消息中搜索其他引文只会给我消息的其他副本,但没有解释。

ilk*_*chu 7

我不想挖掘 25 年前贝壳的来源,但是

它可能是格式字符串漏洞

如果外壳包含类似的代码

printf(str);
Run Code Online (Sandbox Code Playgroud)

哪里str是从用户输入中获取的一些字符串,字符串的内容将是使用的格式字符串printf。在%s我们告诉printf打印一个参数指向一个字符串。如果没有给出参数(如上所述,只有格式字符串),函数将从堆栈中读取一些其他数据,并将它们作为指针跟随。可能访问未映射的内存并使进程崩溃。

在某种程度上,我认为消息的措辞也暗示了这样的解决方案。如果您键入!xxx,shell 明显的作用是打印一条错误消息,如!xxx: event not found. 从那里开始,尝试同时打印 print 并不是一个很大的飞跃!xxx%s%s%s%s%s%s%s%s: event not found,这意味着存在格式字符串漏洞。


我不应该有,但我在这里偷看了一下来源(4.3BSD-Tahoe/usr/src/bin/csh日期是 1988 年)。

findev(cp, anyarg)sh.lex.c外观中,它可能是查找匹配历史事件的函数:它遍历一个struct Hist被调用的链表Histlist。如果它没有找到任何东西,它调用seterr2(cp, ": Event not found");通过noev()cp这里看起来是在历史中搜索的字符串。

seterr2()设置变量err作为参数串联,并且err被用作if (err) error(err);在几个的地方process(),在sh.c。最后,error()(in sh.err.c)包含一个经典的格式字符串漏洞:if (s) printf(s, arg), printf(".\n");

在其他一些地方,error()用一个参数调用,比如error("Unknown user: %s", gpath + 1);,所以很明显,这个想法是第一个参数error()可能是一个格式字符串。

如果我说我完全理解历史替换功能,我不会说实话。它几乎是 C%中未注释的手动字符串处理。在历史替换中确实具有特殊含义,但我只能看到它在第一个字符(如!%)或之后findev()被调用时被特殊处理。