我被链接到Unix 憎恨者手册并偶然发现(第149页):
主题:相关 Unix bug
1991 年 10 月 11 日
W4115x 同学们—
当我们讨论激活记录、参数传递和调用约定时,您是否知道键入:
!xxx%s%s%s%s%s%s%s%s
任何 C-shell 都会导致它立即崩溃吗?你知道为什么吗?
需要思考的问题:
- 当你打字时 shell 会做什么
!xxx
?- 当你打字时它必须对你的输入做什么
!xxx%s%s%s%s%s%s%s%s
?- 为什么这会使外壳崩溃?
- 您如何(相当容易地)重写 shell 的有问题的部分以避免出现此问题?
纯粹出于好奇,谁能解释一下问题是什么?毫不奇怪,在 Google 上搜索该字符串并没有帮助。搜索消息中的其他引用只给了我该消息的其他副本,但没有解释。
答案1
我不想去挖掘25年前的贝壳的来源,但是
它可能是一个格式字符串漏洞。
如果 shell 包含如下代码
printf(str);
其中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
为参数的串联,并在, in 中的几个地方err
使用if (err) error(err);
process()
sh.c
。最后,error()
(在sh.err.c
)包含一个经典的格式字符串漏洞:if (s) printf(s, arg), printf(".\n");
在其他一些地方,error()
是用参数调用的,例如error("Unknown user: %s", gpath + 1);
,所以显然这个想法是第一个参数error()
可能是格式字符串。
如果我说我完全理解历史替换函数,那我是不诚实的。它几乎是 C 中未注释的手动字符串处理。在历史替换中确实有特殊含义,但我只能看到它在调用%
第一个字符(如!%
)或之后被特殊处理。findev()