吹爆SyntaxFlow!数据流分析实战解析
2024-8-15 17:24:5 Author: mp.weixin.qq.com(查看原文) 阅读量:4 收藏

正文开始前辟个谣先
最近有小伙伴来问闭源收费的事
牛牛郑重告知大家
目前还没有这个计划
请大家放心使用
样例解析
在之前的SyntaxFlow教程中,我们已经看到了非常多的代码样例进行数据流分析,这里选用其中一个:
可以看到在代码中,Runtime.getRuntime().exec的参数为simpleBean.getCmd, 而此前也存在simple.setCmdsimple.setCmd2, 通过CmdObject的声明可以知道,getCmd将会拿到this.cmd1,setCmd将会设置this.cmd1,因此exec的参数应该是aTaintCase022的参数cmd
package com.sast.astbenchmark.model;
public class CmdObject { private String cmd1; private String cmd2;
public void setCmd(String s) { this.cmd1 = s; }
public void setCmd2(String s) { this.cmd2 = s; }
public String getCmd() { return this.cmd1; }
public String getCmd2() { return this.cmd2; }}
@RestController()public class AstTaintCase001 { /** * 字段/元素级别->对象字段->对象元素 * case应该被检出 */ @PostMapping(value = "case022") public Map<String, Object> aTaintCase022(@RequestParam String cmd) { Map<String, Object> modelMap = new HashMap<>(); try { CmdObject simpleBean = new CmdObject(); simpleBean.setCmd(cmd); simpleBean.setCmd2("cd /"); var sh = simpleBean.getCmd(); var sh2 = sh; Runtime.getRuntime().exec(sh2); modelMap.put("status", "success"); } catch (Exception e) { modelMap.put("status", "error"); } return modelMap; }}
一级定义
首先我们可以用最基础的use-def链检查一下exec的参数:
Runtime.getRuntime().exec(* as $para)$para #> as $paraDef
得到结果如下: 
注意因为函数exec会传入this参数,因此会出现Runtime.getRuntime() 也存在在参数中。
可以看到加入的var sh2 = sh并没有影响到分析,因为SyntaxFlow使用基于SSA格式的YakSSA HIR,在分析时只关注值的关系,多层的变量传递也只是同一个值并不会影响分析。
同时也可以看到通过->, 可以获取到simpleBean.getCmd() 的上一级引用:函数simpleBean.getCmd和对象simpleBean(这个对象也是被当作this传入的)。
最顶级定义
接下来,我们将使用SyntaxFlow提供的最顶级定义查看exec的参数: 
Runtime.getRuntime().exec(* as $para)$para #-> as $paraDef
得到结果如下:
我们也可以看到分析过程: 
runtime.getRuntime.exec获取参数得到getCmd的调用,这一部分都是通过SyntaxFlow完成的,标注为红色箭头,并且标记了得到该数据的操作。
然后通过数据流分析获取到参数cmd, 在图中使用黑色箭头标记,过程中的点可以看到检查了函数getCmd和调用点。
  样例  
  • 通过{}可以在向上或向下的数据流分析的过程中进行配置。

比如在上述的例子中,我们想要收集在数据流分析过程中的数据语句。可以使用hook,并且继续写一段新的SyntaxFlow的查询语句。
比如如下的例子:
Runtime.getRuntime().exec(* as $para)$para #{hook: `* as $a`}-> as $paraDef
在配置中的hook内可以通过`来写入一段新的SyntaxFlow语句,在数据流追踪过程中的每一个值都会运行该语句,并且该语句支持所有的syntaxFlow特性。
我们可以看到审计结果:
同样我们可以画出该审计过程的图,紫色节点代表当前选中的节点,可以看到他是之前我们审计得到参数cmd过程中的一个节点。
实际使用
接下来是一个Java Servlet的代码样例,在Servlet中规定了doPost/doGet等方法,因此我们可以确定请求的入口是这些函数的第一个参数。但是同时 用户自己编写的代码也可以接收到request类型的参数但是这些函数并不一定会被调用。例子如下:
package net.javaguides.usermanagement.web;
import java.io.IOException;import java.sql.SQLException;import java.util.List;
import javax.servlet.RequestDispatcher;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;
import net.javaguides.usermanagement.dao.UserDAO;import net.javaguides.usermanagement.model.User;
/** * ControllerServlet.java * This servlet acts as a page controller for the application, handling all * requests from the user. * @email Ramesh Fadatare */
@WebServlet("/")public class UserServlet extends HttpServlet { private static final long serialVersionUID = 1L; private UserDAO userDAO; public void init() { userDAO = new UserDAO(); }
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置响应内容类型 resp.setContentType("text/html"); // 从请求中获取参数 String message = req.getParameter("message"); // 获取响应的 writer 对象,用于发送响应数据 PrintWriter out = resp.getWriter(); out.println("<h1>Received POST request with message: " + message + "</h1>"); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getServletPath();
try { switch (action) { case "/insert": insertUser(request, response); break; } } catch (SQLException ex) { throw new ServletException(ex); } }
private void insertUser(HttpServletRequest request, HttpServletResponse response) throws SQLException, IOException { String name = request.getParameter("name"); String email = request.getParameter("email"); String country = request.getParameter("country"); User newUser = new User(name, email, country); userDAO.insertUser(newUser); response.sendRedirect("list"); }}
我们可以从这些函数开始入手获取参数获取到request,并持续向下追踪使用,并配合hook配置获取所有的request.getParameter成员的参数。
/(do(Get|Post|Delete|Filter|\w+))|(service)/(*?{!have: this && opcode: param } as $req);$req.getParameter as $directParam;$req -{ hook: `*.getParameter as $indirectParam`}->$directParam + $indirectParam as $output;$output(, * as $ParamName)
得到结果如下:
预告:YakRunner
END

  YAK官方资源 

Yak 语言官方教程:
https://yaklang.com/docs/intro/
Yakit 视频教程:
https://space.bilibili.com/437503777
Github下载地址:
https://github.com/yaklang/yakit
Yakit官网下载地址:
https://yaklang.com/
Yakit安装文档:
https://yaklang.com/products/download_and_install
Yakit使用文档:
https://yaklang.com/products/intro/
常见问题速查:
https://yaklang.com/products/FAQ


文章来源: https://mp.weixin.qq.com/s?__biz=Mzk0MTM4NzIxMQ==&mid=2247520930&idx=1&sn=9914970dcaf2d8dfc6e02deaba61d35e&chksm=c2d1ee06f5a667102f4db035c8baa8dc7abc5102a48f77d116670e736f7ffa31ed5c27ee05b3&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh