日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

【安全工具】浅谈编写Java代码审计工具

發(fā)布時間:2025/3/21 java 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【安全工具】浅谈编写Java代码审计工具 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

介紹

筆者是大四學生,初涉安全的萌新,如果文章有錯誤之處還請大佬指出!

最初考慮采用純正則等方式匹配,但這種方式過于嚴格,程序員編寫的代碼有各種可能的組合

于是嘗試自行實現(xiàn)Java詞法分析和語法分析,稍作嘗試后發(fā)現(xiàn)這不現(xiàn)實,一方面涉及到編譯原理的一些算法,另外相比C語言等,Java語言本身較復雜,不是短時間能搞定的,深入研究編譯原理背離了做審計工具的目的

后來找到了幾種解決方案:Antlr,JavaCC,JDT,javaparser

經(jīng)過對比,最終選擇javaparser項目,該項目似乎是基于JavaCC,核心開發(fā)者是effective java的作者。使用起來比較方便,可以簡單地以依賴的方式導入

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

筆者本想采用Golang編寫該工具,查找相關資料后發(fā)現(xiàn),Golang本身提供AST庫,可以對Golang本身做語法分析,但找不到實現(xiàn)Java語法分析的庫(考慮后續(xù)復習下編譯原理自己嘗試)

實例

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; }

獲得類節(jié)點

首先拿到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;// 繼續(xù)用lambda反而不方便NodeList<ClassOrInterfaceType> eList = c.getExtendedTypes();for (ClassOrInterfaceType e:eList){if (e.asString().equals("HttpServlet")){isHttpServlet = true;break;}}if (isHttpServlet){// 這里面做進一步的邏輯System.out.println("hello");}});

只有得到類節(jié)點,才可以繼續(xù)遍歷抽象語法樹拿到方法等信息

獲得方法

遍歷得到方法節(jié)點,并且拿到具體的請求和響應參數(shù)名稱

之所以要拿到方法參數(shù)名,是為了做進一步的追蹤

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

確認參數(shù)可控

審計漏洞的關鍵點就在于參數(shù)的可控,這也是難點

就本案例而言,如果某個參數(shù)是req.getParameter("…")獲取的,那么就可以認為是可控

實際上這個req并不一定是req,可能是request,requ等,這也是上一步需要一個map保存的原因

可以加上參數(shù)校驗

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

獲取所有的賦值表達式,確定是否調(diào)用了req.getParameter這樣的參數(shù)

并且參考上文的方式使用map保存這個參數(shù)結果,用于后續(xù)校驗

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"))){// 確定是否調(diào)用了req.getParameterif (right.getName().asString().equals("getParameter")){isGetParam = true;}}}if(isGetParam){var.put("reqParameter",v.getVariables().get(0).getNameAsString());logger.info("find req.getParameter");} });

確定觸發(fā)點

觸發(fā)點在本案例中是resp.getWriter().write()

這是一個方法調(diào)用,所以搜索MethodCallerExpr

m.findAll(MethodCallExpr.class).forEach(im -> {if (im.getScope().get().toString().equals(params.get("response"))) {// 如果調(diào)用了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");}});}}} });

針對于這個基礎案例,可以再加入幾個規(guī)則,針對于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文件分析
  • 可控參數(shù)的追蹤:從controller層傳入?yún)?shù)到返回,這個參數(shù)經(jīng)歷了些什么

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

簡單寫了個輸出html的頁面:

最后

【獲取網(wǎng)絡安全學習資料以及工具】可以關注私我

總結

以上是生活随笔為你收集整理的【安全工具】浅谈编写Java代码审计工具的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产激情无码一区二区 | 中文字幕丰满孑伦无码专区 | 91挑色 | 久久综合一区 | 天天想你免费观看完整版高清电影 | 夜夜草导航 | 天天插视频 | 天天狠天天透 | 日本视频在线播放 | 久久色婷婷| www.中文字幕在线观看 | 尤物193.com| 影音先锋在线播放 | 国产调教| a√天堂网 | 精品香蕉一区二区三区 | 69视频免费在线观看 | 国产亚洲AV无码成人网站在线 | 国产精品海角社区 | 日韩一级高清 | 国产第一色 | 寡妇高潮一级视频免费看 | 久久亚洲av无码精品色午夜麻豆 | 久久国产免费视频 | 中文字幕日韩三级片 | 东北少妇不带套对白 | 人人妻一区二区三区 | 日韩一区二区不卡视频 | 一本色道久久88综合日韩精品 | 极品白嫩丰满少妇无套 | 少妇媚药按摩中文字幕 | 一区二区三区日韩欧美 | 99re在线视频播放 | 亚洲性在线 | 精品久久久亚洲 | 欧美日韩在线国产 | 日韩高清国产一区在线 | av在线网站观看 | 成人拍拍拍 | 亚洲啪| 国产高清在线视频观看 | 欧美一区二区三区色 | 97爱视频 | 黄色网页在线 | 熟女少妇一区二区三区 | 欧美激情国产日韩精品一区18 | 成人免费一区二区三区在线观看 | 精品亚洲天堂 | 精品免费国产 | 成人免费在线电影 | 五月综合在线 | 依人99 | 亚洲精品乱码久久久久久久 | 国产精品白嫩极品美女 | 黄色裸体网站 | 欧美日韩视频在线观看一区 | 国产成人精品一区二区三区无码熬 | 91福利在线观看 | 一区二区三区免费在线视频 | 免费在线观看av | 黄色亚洲精品 | 国产精品乱码久久久久久久久 | 亚洲福利在线观看 | 欧美激情三区 | 夜夜嗨老熟女av一区二区三区 | 色欧美片视频在线观看 | 欧美乱码精品一区二区三区 | 国产成人精品一区在线播放 | 一区二区国产在线 | 亚洲av无码电影在线播放 | 自拍偷拍第3页 | 国产一区二区在线观看视频 | 四虎免费观看 | 亚洲色图2| 日本精品一区二区三区四区的功能 | 日本亚洲欧美在线 | 中文字幕免费在线观看 | 国产色在线观看 | 夜色成人网 | 丰满少妇影院 | 三上悠亚 在线观看 | 免费观看av | 美国福利片 | 中国亚洲老头同性gay男男… | 国产剧情精品 | 澳门黄色一级片 | 糖心logo在线观看 | 狠狠插狠狠操 | 久草成人在线 | 第一区免费在线观看 | 美女又爽又黄视频毛茸茸 | 成人黄性视频 | 国产一区二区黄色 | a天堂v | 久久精品国产一区二区三区 | 日韩123 | 伊人久久大香网 | 99热热| 夜夜撸影院|