简介

NSA的项目 Emissary 中文"密使".
地址是 https://github.com/NationalSecurityAgency/emissary

Emissary是一个基于P2P的数据驱动工作流引擎,运行在异构的、可能广泛分散的、多层的P2P计算资源网络中。
工作流行程不像传统的工作流引擎那样预先计划,而是随着数据的更多信息被发现而被确定(发现)。在Emissary工作流中通常没有用户交互,而是以面向目标的方式处理数据,直到它达到完成状态。

Emissary 是高度可配置的,但在这个"基本实现"(base implementation)中几乎什么都不做。
这个框架的用户应提供扩展emissary.place.ServiceProviderPlace 的类,以在 emissary.core.IBaseDataObject 有效负载上执行工作。

可以做各种各样的事情,工作流是分阶段管理的,例如:STUDY, ID, COORDINATE, TRANSFORM, ANALYZE, IO, REVIEW.

emissary.core.MobileAgent是负责指挥工作流的类、和从它派生的类,它们管理一组相关的负载对象的路径通过工作流。
通过工作流管理一组相关的"负载对象"(payload objects)的路径。

emissary.directory.DirectoryPlace 管理可用的"服务"(services)、它们的成本和质量,并保持 P2P 网络的连接。

参考https://securitylab.github.com/research/NSA-emissary/

不安全的反序列化(CVE-2021-32634) 漏洞1

第一个。
位于WorkSpaceClientEnqueueAction REST端点中。

看代码
https://github.com/NationalSecurityAgency/emissary/blob/30c54ef16c6eb6ed09604a929939fb9f66868382/src/main/java/emissary/server/mvc/internal/WorkSpaceClientEnqueueAction.java#L52

可见WorkSpaceClientEnqueueAction类的实现:

public class WorkSpaceClientEnqueueAction {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /*
     * <!-- Put a file on a work PickUpPlaceClient queue --> <Use-Case source="*" action="/WorkSpaceClientEnque.action">
     * <Work type="Bean" target="emissary.comms.http.worker.LogWorker"/> <Work type="Bean"
     * target="emissary.comms.http.worker.WorkSpaceClientEnqueWorker"/> <View status="0" view="/success.jsp"/> <View
     * status="-1" view="/error.jsp"/> </Use-Case>
     */

    // TODO This is an initial crack at the new endpoint, I haven't seen it called an am unsure when/if it does
    @POST
    @Path("/WorkSpaceClientEnqueue.action")
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.TEXT_PLAIN)
    public Response workspaceClientEnqueue(@FormParam(WorkSpaceAdapter.CLIENT_NAME) String clientName,
            @FormParam(WorkSpaceAdapter.WORK_BUNDLE_OBJ) String workBundleString) {
        logger.debug("TPWorker incoming execute! check prio={}", Thread.currentThread().getPriority());
        // TODO Doesn't look like anything is actually calling this, should we remove this?
        final boolean success;
        try {
            // Look up the place reference
            final String nsName = KeyManipulator.getServiceLocation(clientName);
            final IPickUpSpace place = (IPickUpSpace) Namespace.lookup(nsName);
            if (place == null) {
                throw new IllegalArgumentException("No client place found using name " + clientName);
            }

            final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(workBundleString.getBytes("8859_1")));
            WorkBundle paths = (WorkBundle) ois.readObject();
            success = place.enque(paths);
        } catch (Exception e) {
            logger.warn("WorkSpaceClientEnqueWorker exception", e);
            return Response.serverError().entity("WorkSpaceClientEnqueWorker exception:\n" + e.getMessage()).build();
        }

        if (success) {
            // old success from WorkSpaceClientEnqueWorker
            // return WORKER_SUCCESS;
            return Response.ok().entity("Successful add to the PickUpPlaceClient queue").build();
        } else {
            // old failure from WorkSpaceClientEnqueWorker
            // return new WorkerStatus(WorkerStatus.FAILURE, "WorkSpaceClientEnqueWorker failed, queue full");
            return Response.serverError().entity("WorkSpaceClientEnqueWorker failed, queue full").build();
        }
    }

可以通过对 的经过身份验证的 POST 请求访问此端点/WorkSpaceClientEnqueue.action
正如上面源代码中所看到的,表单参数WorkSpaceAdapterWORK_BUNDLE_OBJ (即tpObj) 在第52行 被解码 并 反序列化。(可以看到readObject)

也就是最关键的这一行final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(workBundleString.getBytes("8859_1")));

幸运的是,这是一个身份验证后的问题(post-authentication issue),由于SonarSource报告修复了跨站请求伪造(CSRF)漏洞,因此无法通过CSRF代表已登录用户利用该漏洞。

CodeQL还报告了另外2个不安全的反序列化操作,这些操作目前没有在代码中执行。然而,它们是定时炸弹,可能在未来的版本中启用,因此安全实验室团队也报告了它们。

第一个起源于MoveToAction类,它没有被Jersey server公开。
正如在comment中描述的:

// TODO This is an initial crack at the new endpoint, I haven’t seen it called an am unsure when/if it does

MoveToAction:
https://github.com/NationalSecurityAgency/emissary/blob/22744ec91ef759078b14975df74a66243f1ce679/src/main/java/emissary/server/mvc/internal/MoveToAction.java

public Response moveTo(@Context HttpServletRequest request)
 final MoveToAdapter mt = new MoveToAdapter();
 final boolean status = mt.inboundMoveTo(request);
 ...

MoveToAdapter:

public boolean inboundMoveTo(final HttpServletRequest req)
 final MoveToRequestBean bean = new MoveToRequestBean(req);
   MoveToRequestBean(final HttpServletRequest req)
   final String agentData = RequestUtil.getParameter(req, AGENT_SERIAL);
   setPayload(agentData);
   this.payload = PayloadUtil.deserialize(s);
  ...

PayloadUtil:

ois = new ObjectInputStream(new ByteArrayInputStream(s.getBytes("8859_1")));

不安全的反序列化(CVE-2021-32634) 漏洞2

第二个方法来源于WorkSpaceAdapter类的inboundEnque方法。
该漏洞需要调用inboundEnque(),但目前尚未执行该调用。

WorkspaceAdapter类:
https://github.com/NationalSecurityAgency/emissary/blob/2e7939f3540f47f0374e77f083af8a657023de35/src/main/java/emissary/server/mvc/adapters/WorkSpaceAdapter.java

/**
   * Process the enque coming remotely over HTTP request params onto the specified (local) pickup client place
   */
  public boolean inboundEnque(final HttpServletRequest req) throws NamespaceException {
    logger.debug("TPA incoming elements! check prio={}", Thread.currentThread().getPriority());
    // Parse parameters
    final EnqueRequestBean bean = new EnqueRequestBean(req);
    // Look up the place reference
    final String nsName = KeyManipulator.getServiceLocation(bean.getPlace());
    final IPickUpSpace place = lookupPlace(nsName);
    if (place == null) {
      throw new IllegalArgumentException("No client place found using name " + bean.getPlace());
    }
    return place.enque(bean.getPaths());
  }

WorkspaceAdapter:

EnqueRequestBean(final HttpServletRequest req) {
      setPlace(RequestUtil.getParameter(req, CLIENT_NAME));
      if (getPlace() == null) {
        throw new IllegalArgumentException("No 'place' specified");
      }
      setPaths(RequestUtil.getParameter(req, WORK_BUNDLE_OBJ));
    }

WorkspaceAdapter:

/**
     * Sets the WorkBundle object from serialized data
     */
    void setPaths(final String s) {
      try {
        final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(s.getBytes("8859_1")));
        this.paths = (WorkBundle) ois.readObject();
      } catch (Exception e) {
        logger.error("Cannot deserialize WorkBundle using {} bytes", s.length(), e);
        throw new IllegalArgumentException("Cannot deserialize WorkBundle");
      }
    }

可以看到readObject

修复与总结

不安全的反序列化会造成安全隐患。

点击收藏 | 0 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖