介绍

笔者是大四学生,初涉安全的萌新,如果文章有错误之处还请大佬指出!

最初考虑采用纯正则等方式匹配,但这种方式过于严格,程序员编写的代码有各种可能的组合

于是尝试自行实现Java词法分析和语法分析,稍作尝试后发现这不现实,一方面涉及到编译原理的一些算法,另外相比C语言等,Java语言本身较复杂,不是短时间能搞定的,深入研究编译原理背离了做审计工具的目的

后来找到了几种解决方案:Antlr,JavaCC,JDT,javaparser

经过对比,最终选择javaparser项目,该项目似乎是基于JavaCC,核心开发者是effective java的作者。使用起来比较方便,可以简单地以依赖的方式导入

<dependency><groupId>com.github.javaparser</groupId><artifactId>javaparser-symbol-solver-core</artifactId><version>3.23.0</version>
</dependency>

笔者本想采用Golang编写该工具,查找相关资料后发现,Golang本身提供AST库,可以对Golang本身做语法分析,但找不到实现Java语法分析的库(考虑后续复习下编译原理自己尝试)

实例

javaparser最根本的类是CompilationUnit,如果我们想对代码做分析,首先需要实例化该对象

// code是读入的java代码字符串
// 也有其他重载,但这个比较方便
CompilationUnit compilationUnit = StaticJavaParser.parse(code);

给出一段最简单的XSS代码

package testcode.xss.servlets;import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class Demo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String param = req.getParameter("xss");resp.getWriter().write(param);}
}

针对于该案例,我们写审计工具的原理

  • 从import来看,比如有request和response,以证明这是HttpServlet
  • 从类来看,必须继承自HttpServlet才能证明这是Servlet
  • 如果req.getParameter得到的值被write了,认为这是XSS

验证导包

关于验证导入包的情况,简单做了一个方法

public static boolean isImported(CompilationUnit compilationUnit, String fullName) {// lambda表达式中必须用这种方式修改值final boolean[] flag = new boolean[1];compilationUnit.getImports().forEach(i -> {if (i.getName().asString().equals(fullName)) {flag[0] = true;}});return flag[0];
}

如果要验证请求和相应包的导入情况

final String SERVLET_REQUEST_IMPORT = "javax.servlet.http.HttpServletRequest";
final String SERVLET_RESPONSE_IMPORT = "javax.servlet.http.HttpServletResponse";boolean imported = isImported(compilationUnit, SERVLET_REQUEST_IMPORT) &&isImported(compilationUnit, SERVLET_RESPONSE_IMPORT);
if (!imported) {logger.warn("no servlet xss");return results;
}

获得类节点

首先拿到Demo这个Class,因为一个java文件中不一定只有一个类

compilationUnit.findAll(ClassOrInterfaceDeclaration.class).stream()// 不是接口且不是抽象类.filter(c->!c.isInterface()&&!c.isAbstract()).forEach(c->{System.out.println(c.getNameAsString());});// 输出
// Demo

进一步,我们需要判断该类是否继承自HttpServlet

compilationUnit.findAll(ClassOrInterfaceDeclaration.class).stream().filter(c->!c.isInterface()&&!c.isAbstract()).forEach(c->{boolean isHttpServlet = false;// 继续用lambda反而不方便NodeList<ClassOrInterfaceType> eList  = c.getExtendedTypes();for (ClassOrInterfaceType e:eList){if (e.asString().equals("HttpServlet")){isHttpServlet = true;break;}}if (isHttpServlet){// 这里面做进一步的逻辑System.out.println("hello");}});

只有得到类节点,才可以继续遍历抽象语法树拿到方法等信息

获得方法

遍历得到方法节点,并且拿到具体的请求和响应参数名称

之所以要拿到方法参数名,是为了做进一步的追踪

if (isHttpServlet){c.getMethods().forEach(m->{// lambda不允许直接复制,所以借助mapMap<String,String> params = new HashMap<>();m.getParameters().forEach(p->{// resp(真实情况未必一定是resp)if (p.getType().asString().equals("HttpServletResponse")) {params.put("response", p.getName().asString());}// req(真实情况未必一定是req)if (p.getType().asString().equals("HttpServletRequest")) {params.put("request", p.getName().asString());}});System.out.println("request:"+params.get("request"));System.out.println("response:"+params.get("response"));});
}// 输出
// request:req
// response:resp

确认参数可控

审计漏洞的关键点就在于参数的可控,这也是难点

就本案例而言,如果某个参数是req.getParameter("…")获取的,那么就可以认为是可控

实际上这个req并不一定是req,可能是request,requ等,这也是上一步需要一个map保存的原因

可以加上参数校验

if (params.get("request") != null && !params.get("request").equals("") ||params.get("response") != null && !params.get("response").equals("")) {return;
}

获取所有的赋值表达式,确定是否调用了req.getParameter这样的参数

并且参考上文的方式使用map保存这个参数结果,用于后续校验

Map<String,String> var = new HashMap<>();
m.findAll(VariableDeclarationExpr.class).forEach(v->{MethodCallExpr right;boolean isGetParam = false;// 获取赋值语句右边部分if (v.getVariables().get(0).getInitializer().get() instanceof MethodCallExpr) {// 强转不验证会出问题right = (MethodCallExpr) v.getVariables().get(0).getInitializer().get();if (right.getScope().get().toString().equals(params.get("request"))){// 确定是否调用了req.getParameterif (right.getName().asString().equals("getParameter")){isGetParam = true;}}}if(isGetParam){var.put("reqParameter",v.getVariables().get(0).getNameAsString());logger.info("find req.getParameter");}
});

确定触发点

触发点在本案例中是resp.getWriter().write()

这是一个方法调用,所以搜索MethodCallerExpr

m.findAll(MethodCallExpr.class).forEach(im -> {if (im.getScope().get().toString().equals(params.get("response"))) {// 如果调用了response.getWriterif (im.getName().asString().equals("getWriter")) {MethodCallExpr method;// 直接强转会出问题if (im.getParentNode().get() instanceof MethodCallExpr) {// 后一步方法method = (MethodCallExpr) im.getParentNode().get();} else {return;}// response.getWriter.write();if (method.getName().asString().equals("write")) {// 该案例中write的是常量param,所以搜NameExprmethod.findAll(NameExpr.class).forEach(name -> {// 这里用到了之前保存在map的reqParameterif (name.getNameAsString().equals(var.get("reqParameter"))) {// 认为存在XSSlogger.info("find xss");}});}}}
});

针对于这个基础案例,可以再加入几个规则,针对于response.getOutputStream方式

if (im.getName().asString().equals("getOutputStream")) {MethodCallExpr method;if (im.getParentNode().get() instanceof MethodCallExpr) {method = (MethodCallExpr) im.getParentNode().get();} else {return;}// response.getOutputStream.print();// response.getOutputStream.println();if (method.getName().asString().equals("print") ||method.getName().asString().equals("println")) {method.findAll(NameExpr.class).forEach(name -> {if (name.getNameAsString().equals(var.get("reqParameter"))) {logger.info("find xss");}});}
}

测试

尝试让原来的XSS代码复杂一些,看看审计的效果

public class Demo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String param = req.getParameter("xss");if(param.equals("hello world")){// do other}else{demoService.doSearch();}int a = 1;int b = 2;logger.log(String.format("%d+%d=%d",a,b,a+b));try{// todo}catch (Exception e){e.printStackTrace();}resp.getWriter().write(param);}
}

运行后成功检测到XSS

结尾

这篇文章只针对最基本的Servlet XSS做了审计,实际上无论从广度还是深度,都有巨大的工作量:

  • 广度:SQL注入,XXE,反序列化,文件上传,CSRF等漏洞的审计
  • 深度:如果代码对Servlet做了一定的封装,或者需要跨多个java文件分析
  • 可控参数的追踪:从controller层传入参数到返回,这个参数经历了些什么

代码在github:https://github.com/EmYiQing/XVulnFinder

简单写了个输出html的页面:

最后

【获取网络安全学习资料以及工具】可以关注私我

【安全工具】浅谈编写Java代码审计工具相关推荐

  1. 浅谈对java编程思想的理解

    浅谈对java编程思想的理解 刚从学校毕业的时候,对于这种概念的理解少之又少 ,只是单纯的从事编码工作,理解也只是停留在对java基本概念的使用上,很局限!随后工作了快三年的时间里,自己不断的理解这种 ...

  2. 解耦 多态性 java_Java的多态浅谈,Java多态浅谈网站安全分享!

    Java的多态浅谈概述Java的四大基本特性:抽象,封装,继承和多态.其中,抽象,封装,继承可以说多态的基础,而多态是封装,继承的具体表现.如果非要用专业术语来描述什么是多态的话 多态是指程序中定义的 ...

  3. CAN诊断浅谈 如何用CAN工具进行诊断

    转:https://mp.weixin.qq.com/s/bcuoUQfkfyx6rtbgw-Z_BQ 引言: 从之前的课程我们了解到, 车上的智能硬件设备太多, 即ECU节点太多, 这些节点通过CA ...

  4. 嘴哥有料系列-can工具1:CAN诊断浅谈 + 如何用CAN工具进行诊断

    原文章:https://mp.weixin.qq.com/s/bcuoUQfkfyx6rtbgw-Z_BQ 引言: 从之前的课程我们了解到, 车上的智能硬件设备太多, 即ECU节点太多, 这些节点通过 ...

  5. 浅谈计算机实用教学工具应用的体会,浅谈计算机常用工具软件教学心得.doc

    浅谈计算机常用工具软件教学心得 浅谈计算机常用工具软件教学心得 摘 要:随着计算机技术的普及,社会对掌握计算机技术的人才不断提出新的要求,人们所需要掌握的计算机技术也越来越多.<计算机常用工具软 ...

  6. 浅谈敏捷开发scrum工具leangoo(三)

    之前两篇有说到敏捷开发及Scrum的一些概念及实施,现在再来记录下我们在敏捷开发实施中使用的一些工具,主要说下leangoo 工具不是敏捷开发及Scrum的必须品,但有了工具,可以让敏捷开发更好的实施 ...

  7. 「技术工具」阿里开源Java在线诊断工具 Arthas 进阶教程

    Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱. 本教程会以一个普通的Spring Boot应用为例,演示Arthas命令的详细用法. Github: https://githu ...

  8. 教师教学常用计算机软件,浅谈《计算机常用工具软件》的教学心得

    摘 要:随着计算机技术的普及,社会对掌握计算机技术的人才不断提出新的要求,人们所需要掌握的计算机技术也越来越多.<计算机常用工具软件>具有实践性.应用性.灵活性等特点,旨在培养职高学生能够 ...

  9. shell浅谈之七文本处理工具grep、sed、awk

    一.简介 Bash Shell提供了功能强大的文件处理工具:sed(流编辑器stream editor)和awk,都可使用正则表达式进行模式匹配.而grep又有助于理解sed和awk. 二.grep命 ...

最新文章

  1. adobe就不敢把融合做得更好一点?
  2. 我的第一篇博客,以此写写内心的独白
  3. DeepLearning:windows环境下C++环境实现Tensorflow编译部署
  4. 数据结构【插入操作具体代码的实现】
  5. 使用 @ControllerAdvice 和 实现ResponseBodyAdvice接口, 拦截Controller方法默认返回参数,统一处理返回值/响应体
  6. 新特性 | Java8 的这个特性,用起来真的很爽!
  7. 【转】字符编码笔记:ASCII、Unicode、UTF-8 和 Base64
  8. android seekbar 代码设置高度,Android - 如何更改默认的SeekBar厚度?
  9. OllyDbg 与 x64Dbg 与 Windbg 与 IDA 区别是什么?
  10. Word中插入的Excle打开后闪退如何解决? - Office2019
  11. C罗是你人生中最好的健身教练和精神导师
  12. debian7 修改系统时间
  13. 数据分析--分类与回归模型(一)
  14. mysql : 使用不等于过滤null的问题
  15. 软件工程-- 层次方框图
  16. 数据库基础语法--php基础最详细教程
  17. window10 安装语言包出现“很抱歉,我们无法安装此功能。你可以稍后重试。错误代码: 0x80070422”
  18. 深入理解vue slot插槽
  19. 贾樟柯赵涛宣布结婚:8月已领证 威尼斯拍婚照
  20. 电力-故障录波(向量图)

热门文章

  1. Dataset之LSUN:LSUN数据集的下载使用教程
  2. NLP:NLP领域没有最强,只有更强的模型——GPT-3的简介、安装、使用方法之详细攻略
  3. DL:深度学习算法(神经网络模型集合)概览之《THE NEURAL NETWORK ZOO》的中文解释和感悟(二)
  4. 成功解决cv2.error: C:\projects\opencv-python\opencv\modules\imgproc\src\resize.cpp:4044: error: (-215) s
  5. 十一、linux文件系统权限详解
  6. [POI2007]ZAP-Queries [HAOI2011]Problem b 莫比乌斯反演
  7. NOI Day1线上同步赛梦游记
  8. MySQL慢查询日志ES索引模板
  9. 我的第一份vPlan衍变路线
  10. 在多台服务器上简单实现Redis的数据主从复制