jdi自动代码审计分析的可能 | 宜武汇-ag真人国际厅网站

最近更新项目差不多了,感觉项目的大部分问题总算得到一个总体的解决。对象分析真的太要命了
github:

我经常对半自动化代码审计的工具会有个小小的问点,无论是现今所谓的静态代码分析还是自动化代码分析工具,他们的注重点永远的都是挖掘源代码的漏洞。但是很多情况下会有到这样的问题:

public class testmain{ public void testmain(httpservletrequest req, httpservletresponse resp){ string id = req.getparameter("info"); runtime.getruntime().exec(id); } }

理论上来说,这个类一定会被工具分析出来的,但是问题来了,产品的某个api节点会调用testmain.test方法吗?这显然要打个问号?所以我觉得首先必须要把哪些类才是api节点的入口都统一整理出来。只有这样,我觉得在配合像codeql这样的工具时,可以避免很多多余的告警或者没有意义的结果。所以我从去年的漏洞挖掘中已经在做这么一个事情了,当前做这个事情有很多的ag真人国际厅网站的解决方案:
● 静态分析配置文件: 这个方案很糟糕,比如filter或者serlvet的注入是有可能在代码运行中才注入(jersey这样无配置可分析的,将是个无解的方案)
● java agent: 技术实在难度大, 难点在怎么增强字节码(类和路由的注册时间不一样的)、项目兼容问题
● jdwp的调试方案: 接口调用简单,难点在对象分析

静态代码分析路由配置文件

以下路由都不在配置文件的范畴:
● spring的注解路由
● jersey的注解路由
● @servlet
● 代码层面的路由修改(动态增加war/动态添加servlet/动态增加spring路由等等)
可见,效果极差

java agent

java agent本质是字节码增强,方案比较推荐aop某个方法:在注册路由和处理类时,加一个记录功能。但是有些坑爹的地方:
● java agent开发过程中如果引入了一个依赖包,这个依赖包和生产的依赖包发生冲突怎么办?
● java agent版本可能和目标产品的jvm是有不兼容的问题,比如java agent编译的版本过高了怎么办?
基于以上两点,我并没有采用。我相信世界这么多产品,你不可能都能完美避免

jdwp

也许经常用idea做调试吧,我开始关注起jdi的相关技术了,至少在以下情况,它能做得更好:
● 独立运行,意味着它不存在和产品兼容性绑定的问题
● 独立运行,依赖完全自由控制,甚至在一个springboot项目中存在
● 由于是调试技术,可以不用像静态分析那样做些反编译的工作
花小部分时间学习一些api能解决我的问题,我觉得不算什么

模拟调试

如果做调试的,经常会在目标产品中添加这么一个启动参数:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

那么作为调试器该如何连接调试端口5005呢?
答案是socketattachingconnector

socketattachingconnector socketattachingconnector = new socketattachingconnector(); map argumenthashmap = socketattachingconnector.defaultarguments(); argumenthashmap.get("hostname").setvalue("127.0.0.1"); argumenthashmap.get("port").setvalue("5005"); argumenthashmap.get("timeout").setvalue("3000"); virtualmachine attach = socketattachingconnector.attach(argumenthashmap);

在完成上面的代码时,你将获得一个virtualmachine的对象,这是一个目标jvm的一个引用,此时你可以对目标做以下操作:
● 添加断点
● 搜索目标加载的类/类对象
● 对目标加载的类做一些操作:比如调用runtime.getruntime().exec(”)(出于安全考虑,工具默认ip限制127.0.0.1)
这里先从以下几个方面分析究竟哪种方式更加有利于分析

添加断点

这是我最开始的想法,但很快得到了痛击,为什么?你要知道,添加断点意味着首先你要等待一个断点调试的事件,我的天啊,这意味着你要发送请求去触发断点。我举个例子:


http://127.0.0.1:8080是一个基于tomcat的java web服务。你发送http://127.0.0.1:8080时,你是一定能触发org.apache.catalina.mapper.mapper的internalmap方法,因为你一定会触发tomcat寻找路由这一件事情。但是很可惜的是,springmvc/jersey/struts呢?怎么确保能触发他们各自的寻找路由的请求呢?很显然,困难摆在你的面前了。也许你会说我发送一个http://127.0.0.1:8080/spring/api请求不就行了吗?我们再看看以下的示例图吧


工具太难构造出这样的请求。如果没有正确的token,你根本到不了spring的路由分析调试点,所以这意味着你的工具要一直处于一个连接调试的状态,把每一个功能点都走完,才算分析完成。这其实是有点失败的,因为这一点都不智能。而且还有一个很大的问题:这种基于断点的调试,在大一点的项目还很容易卡死!(原因很简单,你卡着一个断点然后花了一秒钟的时间做处理,但是你在浏览器访问时,经常会在一个时间发送大量的请求,这意味剩下的断点都卡着等你,一等你,浏览器觉得卡了也会发送尝试的请求过来,导致越来越多的请求)所以在router-router中经常会崩溃。

致命弱点

  • 需要一个调试事件的到来,没有调试事件就无法分析路由
  • 通过jdi调试的断点是只能在一个类中下断点的。注意,当你调试一个父类时,子类触发的方法,父类是跟踪不到的!所以如果目标产品继承了org.springframework.web.servlet.dispatcherservlet,工具就是个摆设。
  • 无法单线程完成一个断点调试事件的分析,必须多线程处理每一个调试事件

目标jvm搜索类与类对象

这是我觉得现今最稳妥的方案了。因为此时工具已经不再等待调试信息了,你可以直接通过jdi接口调用的形式获得某个类对应的所有实例对象:

virtualmachine attach; list refs = attach.classesbyname("org.apache.catalina.mapper.mapper"); list instances = refs.get(0).instances(0);

此时你就拿到目标jvm所有的org.apache.catalina.mapper.mapper对象,当然有时候你会获取很多的org.apache.catalina.mapper.mapper,因为存在对象销毁重新创建的可能。还有那些游离还未被gc处理的对象,你也会在这时刻一起拿到。虽然存在重复分析的可能,但是至少能完美达到我想要的结果。
那么spring/jetty/jersey/struts/tomcat要分析哪些对象呢?

//spring org.springframework.web.servlet.handler.beannameurlhandlermapping org.springframework.web.servlet.mvc.support.controllerbeannamehandlermapping org.springframework.web.servlet.mvc.support.controllerclassnamehandlermapping org.springframework.web.servlet.handler.simpleurlhandlermapping org.springframework.web.servlet.mvc.method.annotation.requestmappinghandlermapping //jetty org.eclipse.jetty.webapp.webappcontext org.eclipse.jetty.servlet.servletcontexthandler //jersey org.glassfish.jersey.servlet.servletcontainer //1.x com.sun.jersey.spi.container.servlet.servletcontainer //2.x //struts org.apache.struts.config.impl.moduleconfigimpl //1.x com.opensymphony.xwork2.config.impl.defaultconfiguration //2.x //tomcat org.apache.catalina.mapper.mapper // tomcat8/9 org.apache.tomcat.util.http.mapper.mapper // tomcat 6/7

致命弱点

目标搜索的对象,你将失去方法调用的权利,因为jdi在调用方法时是一定要在一个线程环境中运行的,然而通过内存搜索的方式,你是没有设置断点,没有断点事件就意味着你得不到一个线程环境,所以你只能分析对象的结构,无法调用对象的任何方法,这也是为什么router4.x版本中只有tomcat有version(tomcat可以不用调用方法获得版本号), 所以在使用idea做调试时,如果没有到断点的时候,你压根没法执行表达式的原因。

思考的问题

基本的思路已经确定,我们是不是可以继续延申:

  1. 既然断点调试不适用做路由分析,我们可不可以让断点调试做一些监控工作,我们可不可在io/runtime.getruntime()这样的类上下断点,是不是继续扩展了jdi强大的功能?这样我们得到了一个类似于污染点分析工具,这样更加准确地捕捉哪些api会触发io读写的操作,哪些api会触发命令执行的操作,哪些api会触发sql查询。
  2. 既然能执行方法,我们能不能获取某个类的字节码做一些反编译的工作,这样我们就不用自己dump内存的字节码了
    最后,我觉得jdi还是有发挥空间。

原文链接:https://xz.aliyun.com/t/12651

网络摘文,本文作者:15h,如若转载,请注明出处:https://www.15cov.cn/2023/08/27/jdi自动代码审计分析的可能/

发表评论

邮箱地址不会被公开。 必填项已用*标注

网站地图