这个Perl代码中的特定路径连接是否存在漏洞?

3
假设攻击者控制变量$untrusted_user_supplied_path,下面的Perl代码是否可被利用?
my $untrusted_user_supplied_path = ...
if ($untrusted_user_supplied_path =~ /\.\./) {
  die("Tries to escape homedir.");
}
my $base_path = "/home/username/";
my $full_path = "${base_path}${untrusted_user_supplied_path}";
if (-e $full_path) {
  open(FILE, "<", $full_path) || die("File not accessible.");
  while (<FILE>) {
    # present the content to the user
  }
  close(FILE);
}

如果攻击者可以选择一个$untrusted_user_supplied_path的值,以便读取不是$base_path子目录中的文件(例如/etc/passwd),那么该代码被定义为可利用。
您可以假设该代码正在Linux下运行。此外,您可以假设在向用户展示文件的代码中没有引入额外的缺陷。
请注意,问题是关于代码是否可利用,而不是如何使代码更安全。有许多方法可以使代码更安全(想想chroot等),但这超出了本问题的范围。只需在您的答案中说明您是否认为该代码是可利用的。当然,请提供支持性的论据。

4
这个问题实际上与Perl无关,而与文件系统安全和目录路径的指定方式有关。 - Ether
1
那么,我认为链接应该是你唯一关心的事情。 - Geo
3
为什么这个被投票踩了这么多?(目前是-3) - Jeremy Powell
1
我没有看到任何理由应该将此投票降低。+1 - Jeremy Powell
1
OP似乎对回答他问题的人很刻薄。无论是否合适,这几乎总是会让你得到负分。我个人会倾向于将其关闭为“不是一个真正的问题”,但既然有人在回答它,我想这是一个真正的问题。 - jrockway
显示剩余7条评论
5个回答

9
如果在主目录中存在指向外部某个位置的符号链接,你仍然会遇到麻烦。

9
您在询问您的代码是否存在漏洞。是的,所有的代码都有可能被攻击。您可能认为您已经考虑到了所有情况,但通常对手会找到您没有想到的情况。但是,我总是说所有枪支都是装填好的。
安全不仅仅是代码。您必须考虑它运行的环境,用户在运行您的代码之前允许做了什么等等。
如果您真的担心这段代码可能会发生什么事情,请创建一个风险矩阵。从您担心的部分开始,列出所有的假设。例如,在您的情况下,您可以从以下内容开始:
  • /home/username 是我认为的目录(即不是挂载点、符号链接、虚假用户等)
  • 提供的路径是我预期的并且允许存在的
  • 路径是一个普通文件(例如不是特殊设备)
  • 路径具有某些所有者、组或模式
  • 我正在运行我认为的 perl(在查找可执行文件时没有路径攻击)
  • PERL5LIBPERL5OPT-I 没有前置模块加载路径(在查找模块时没有路径攻击)

等等。一旦您开发出所有假设,您通过锁定这些情况来确保它们有效。您还会找到它们的所有假设,并锁定那些假设,依此类推。Perl 的taint 检查将有助于其中一些(我在精通 Perl中对其进行了更深入的讨论)。

成功的攻击通常是间接的。例如,我曾参与保护一个非常富有和多疑的银行的一些数据的工作。我们尽了所有我们能做的计算机工作,我的一个同事在闲聊中问到我们安装服务器之前他们是如何完成这项任务的。他们说,“哦,数据在某人桌子上的一个文件夹里”。尽管我们做了所有的工作,付出了他们的薪水和每个人的时间和努力,但任何想要这些数据的内部人员都可以真正地走开,无论我们对服务器做了什么。
现在你已经有了你的风险矩阵,你开始制定你的风险容忍度。没有什么是完美的,你可以锁定每一个东西直到宇宙的热死亡。与其完美,你选择为代码的每个部分承担多少风险。你找出如果某个部分被攻击可能会发生什么,以及那会给你(或你的雇主)带来多大的损失(以美元、声誉等),并计算出那值得你(或你的雇主)投入多少工作。你只做足够低于你的风险容忍度的工作量。
问题是即使最好的人也会错过某些东西。安全上的小裂缝可能看起来并不重要,但如果你把足够多的裂缝放在一起,最终你就可以将自己引导到一个可利用的情况中。安全是整体的。

感谢Brian的回答!像往常一样,回答写得非常好。我真的很喜欢你的Perl答案。 - knorv

1

我要违反你的房屋规定,建议你这样做:

use Cwd;
my $full_path = "${canonical_base_path}${untrusted_user_supplied_path}";
my $canonical_full_path = abs_path($full_path);
if (substr($canonical_full_path, 0, length($base_path)) != $base_path) {
      die("Tries to escape homedir.");
}

这应该是完全密封的。但它需要 $base_path 是规范的。


抱歉,但这不是问题的答案。如果我问了“如何改进这段代码”,那么我会将您的消息标记为正确答案。 - knorv
2
你的 != 应该改成 ne,不应该吗? - Geo
1
什么数学比较?$base_path 是“/home/username”。该代码试图确保 $base_path 在字符串开头(虽然使用 index(...) == 0 更容易和更清晰)。 - brian d foy
我的 Perl 已经非常生疏了,我很担心。我主要使用 shell 脚本,其中 != 用于字符串,-ne 用于数字 - 如果在 Perl 中反过来了,那么是的,应该使用 -ne。 - Tom Anderson

1

对我来说,这看起来是合理的,虽然你的测试有点过于严苛。你可能想考虑替换为:

/\.\./

使用:

m{/\.\./}

允许访问包含两个点的文件和目录。但它仍然不会允许复杂但可能有效的访问,例如dir1/../dir2/filename,尽管您可能不太担心这个问题。


Tim,感谢您的回答。我的理解是“不,代码是无法利用的”,这样对吗? :-) - knorv
我不会那么自信断言,但在有人指出我可能忽略的问题之前,我对那段代码感到满意。 :-) - Tim
2
注意,在Windows下,三个点也可以表示目录遍历。 - bobince
2
你看,这就是我担心的那种评论。 :-) - Tim
直到有人指出我漏掉的问题,我会对这段代码感到满意。这是可能存在的最大安全隐患。如果你真的关心安全,你应该时刻警醒自己可能会有遗漏,并且永远不会感到满足或满意。平庸就是安全的反面。 - brian d foy
显示剩余2条评论

0

它是否可利用取决于呈现文件给用户的代码。那段代码不必有“缺陷”,而是提供了一些你没有想到过的操作机会。


假设代码向用户展示文件时不会引入任何其他缺陷,基于这个假设,你的答案是什么? - knorv
1
这并不是“缺陷”的问题。 - user181548
1
问题非常清晰,而这不是它的答案。 - Tom Anderson
3
这不是你想象的那么简单。 - user181548
1
你可以假设没有其他缺陷。我听过这种说法很多次,它总是意味着“其他所有的东西都存在严重的安全问题,而我们只寄希望于这一行代码。” - brian d foy
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接