wJa丨Java闭源项目的自动化测试「建议收藏」

wJa丨Java闭源项目的自动化测试「建议收藏」本文是i春秋论坛作家「Wker」表哥分享的技术文章,文章旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。wJa支持反编译Java生成的jar包文件,整理成语法树,根据调用链进行污点分析

大家好,又见面了,我是你们的朋友全栈君。

 
wJa丨Java闭源项目的自动化测试「建议收藏」

 

本文是 i 春秋论坛作家「Wker」表哥分享的技术文章,文章旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。

wJa支持反编译Java生成的jar包文件,整理成语法树,根据调用链进行污点分析,通过cheetah脚本语言编写测试脚本,确定可能存在的漏洞调用链,生成测试链接,进行fuzzer测试。

下面对网络上的一个Spring靶场进行SQL注入的分析测试。

环境准备

1、测试靶场

2、Java运行环境

3、wJa

wJa分析流程

解析jar包

解析class文件结构

反编译得到AST

优化AST

生成Java代码

编写调用链追踪

过滤函数剪枝

黑盒测试Fuzzer

运行靶场

搭建好靶场环境,这里使用的靶场是在网上寻找到的,里面有创建数据库的脚本文件,环境一切准备就绪后。

运行靶场,使用命令跑起jar包:java -jars shootingRange.jar



wJa丨Java闭源项目的自动化测试

 

使用wJa打开jar文件

使用命令:java -jar wJa.jar运行起程序,程序运行之后会要求选择待分析的jar程序,这里选择shootingRange.jar。

起来之后可以看到程序的主界面了。



wJa丨Java闭源项目的自动化测试

 

左侧部分:

Decompile:对jar包反编译的java资源管理器。

cheetahLanguage:脚本管理器,包含支持库介绍,以及编写好的cheetah脚本。

中间部分:

Decompile:对jar包反编译的java代码显示部分。

cheetahLanguage:编写cheetah脚本代码,运行测试。

界面相对比较简单。

我们可以先看整个靶场的一个框架结构,从control层进行分析(Spring默认路径为BOOT-INF/classes)。



wJa丨Java闭源项目的自动化测试

可以看到control层都是提供对外开放的接口,所以我们可以确定这是入口类,所以我们可以将其确定为入口点。

从其中的一个入口点(one)根据一步步的调用追踪,我们可以得到如下调用链:

indexLogic.getStudent(username);
indexDb.getStudent(username);
sql = "select * from students where username like '%" + username + "%'";
jdbcTemplate.query(sql, ROW_MAPPER);

  

最终进入的危险函数:

wJa丨Java闭源项目的自动化测试

 

当然,这是我们手动跟踪的,但是如何使用工具自动帮助我们进行追踪呢?

编写白盒污点跟踪代码

污点分析

污点分析可以抽象成一个三元组〈sources, sinks, sanitizers〉的形式, 其中, source即污点源, 代表直接引入不受信任的数据或者机密数据到系统中;

sink即污点汇聚点, 代表直接产生安全敏感操作 (违反数据完整性) 或者泄露隐私数据到外界 (违反数据保密性);

sanitizer即无害处理, 代表通过数据加密或者移除危害操作等手段使数据传播不再对软件系统的信息安全产生危害。

污点分析就是分析程序中由污点源引入的数据是否能够不经无害处理, 而直接传播到污点汇聚点。如果不能, 说明系统是信息流安全的; 否则, 说明系统产生了隐私数据泄露或危险数据操作等安全问题。

对于SQL注入这种漏洞,可以将污点分析的三元组实例化为下面三组内容:

source:Spring的接口入口点的参数;

sink:jdbc的query方法;

sanitizer:类似于Integer.value此类方法。

代码编写

在检测类似于SQL注入类漏洞,我们需要的是跟踪调用链,所以需要使用的是TrackVarIntoFun函数。

TrackVarIntoFun

参数1:起始类
参数2:起始方法
参数3:起始方法参数下标
参数4:目标方法的类
参数5:目标方法
参数6:目标方法的参数下标
返回值:执行流node数组

1、起始类是我们需要分析的类,这里是
com/l4yn3/microserviceseclab/controller/IndexController

2、起始方法是入口方法,也是这个类下面的所有接口方法

3、起始方法参数下标是要检测的入口参数下标

4、目标方法类是jdbc,这里是
org/springframework/jdbc/core/JdbcTemplate

5、目标方法是query,jdbc查询数据的方法

6、目标方法的参数下标是第一个参数,sql语句

返回值是一个node执行流数组,node包含次node所在的class和node的AST。

我们设置开头的包名,那如何获取所有的方法名呢?

GetAllMethodName可以获取所有的方法名称,但是这里有一个注意的地方是,如果方法名是<init>和<cinit>的需要跳过,因为这两个方法是构造方法和静态代码块。

node中的AST可以通过GetJavaSentence方法得到对应生成的java代码。

还有一点需要注意的是,TrackVarIntoFun方法只是跟踪流,只是到目标方法就停止,如果没有到目标方法就停止了那么也是会返回所有的执行流,所以这里我们需要自己进行过滤。

所以现在的思路已经完成,通过GetAllMethodName获取所有的方法,然后对方法中的第一个参数进行追踪,查看其最终流向的是否是jdbc,并且判断流动过程中是否有类似于Integer.value()方法的存在,如果不存在,那噩梦非常可能就是一条可以被污染的链条。

最终我们可以编写出如下代码:

#define filter1=String.valueOf(.*?)
#define filter2=Integer.valueOf(.*?)
function filter(sentence){
    a = StrRe(sentence,filter1);
    if(GetArrayNum(a) != 0){return 0;}
    a = StrRe(sentence,filter2);
    if(GetArrayNum(a) != 0){return 0;}
    return 1;
}
function track(className,methodName){
    array allNode;
    allNode = TrackVarIntoFun(className,methodName,0,"org/springframework/jdbc/core/JdbcTemplate","query",0);
    size = GetArrayNum(allNode);
    if(StrFindStr(GetJavaSentence(allNode[ToInt(size-1)]),".query(",0) != "-1"){
        i = 0;
        print(methodName."参数流动:");
        cc = 7;
        cs = 1;
        while(i < size){
            sentence = GetJavaSentence(allNode[i]);
            if(filter(sentence) == 0){cc = 5;cs = 5;printcolor("想办法绕过此类:",4);}
            if(i == ToInt((size-1))){
                if(cc != 5){cs = 2;cc = 3;}
            }else{}
            if(cc == 5){printcolor("[-]",6);}else{printcolor("[+]",1);}
            printcolor(GetClassName(GetNodeClassName(allNode[i]))."   ",cc);
            printcolor(sentence.StrRN(),cs);
            i = ToInt(i+1);
        }
    }
    return 0;
}
function main(args){
    className = "com/l4yn3/microserviceseclab/controller/IndexController";
    methods = GetAllMethodName(className);
    size = GetArrayNum(methods);
    i = 0;
    while(i < size){
        if(methods[i] != "<init>"){track(className,methods[i]);
}
        i = ToInt(i+1);
    }
}

  

如果对cheetah语法不熟悉,那么可以到https://github.com/Wker666/Demo中了解cheetah的详细语法,里面含有500+的渗透测试脚本可供学习。

最终我们白盒审计可以打印出如下内容:

wJa丨Java闭源项目的自动化测试

 

可以看到我们对没有过滤的调用链都进行了高亮显示,对有过滤的用红色进行显示。

编写黑盒Fuzzer测试代码

SQL注入检测函数

我们可以使用简单or 1=1与or 1=2进行判断,为什么不能用and呢?因为我们没有默认值,所以需要通过or进行判断。

function judgeSQLI(api){
    res = HttpGet(api,"");
    res1 = HttpGet(api."%27%20or%201=1--+","");
    if(GetStrLength(res1[0]) != GetStrLength(res[0])){
        res2 = HttpGet(api."%27%20or%202=1--+","");
        if(GetStrLength(res2[0]) == GetStrLength(res[0])){
            return 1;
        }
    }
    return 0;
}

 

如果你想要了解更多的cheetah编写sql注入的代码,可以看cheetah的GitHub,里面是有一个非常完整的SQL注入脚本代码的。

组成测试链接

因为Spring中使用大量注解进行设置,对于注解的解析,wJa提供了获取注解的方法。

1、GetClassAnnotation获取类注解

2、GetClassMethodAnnotation获取方法上的注解

3、GetClassMethodArgAnnotation获取参数上的注解

4、GetAnnotationArgListValue获取注解中list数据

5、GetAnnotationArgSingValue获取注解中的数据

通过上述的注解方法我们可以构造完整的测试链接,当然我们可以编写一个参数进行解析注解参数数据。

function getSpringAnnotationValue(an){
    anSize = GetArrayNum(an);
    i = 0;
    while(i < anSize){
        if(GetAnnotationName(an[i]) == "org/springframework/web/bind/annotation/RequestMapping"){
            allValue = GetAnnotationArgListValue(an[i],"value");
            return allValue[0];
        }
        if(GetAnnotationName(an[i]) == "org/springframework/web/bind/annotation/PostMapping"){
            allValue = GetAnnotationArgListValue(an[i],"value");
            return allValue[0];
        }
        if(GetAnnotationName(an[i]) == "org/springframework/web/bind/annotation/RequestParam"){
            allValue = GetAnnotationArgSingValue(an[i],"value");
            return allValue;
        }

        i = ToInt(i + 1);
    }
    return "";
}

根据Spring的的注解,我们得到路径中的某一个值。

因为我们已经开启了8080端口的javaWeb服务,所以可以直接进行拼接组合成测试链接。

白盒+黑盒 自动化测试

有了白盒测试和黑盒测试的代码部分,我们可以进行组装拼接,当白盒测试代码发现没有过滤函数,并且最终进入了危险函数,那么我们就启动黑盒测试进行真正意义上的Fuzzer。

这里附带完整的白盒+黑盒 自动化测试脚本。

#define filter1=String.valueOf(.*?)
#define filter2=Integer.valueOf(.*?)
function filter(sentence){
    a = StrRe(sentence,filter1);
    if(GetArrayNum(a) != 0){return 0;}
    a = StrRe(sentence,filter2);
    if(GetArrayNum(a) != 0){return 0;}
    return 1;
}
function judgeSQLI(api){
    res = HttpGet(api,"");
    res1 = HttpGet(api."%27%20or%201=1--+","");
    if(GetStrLength(res1[0]) != GetStrLength(res[0])){
        res2 = HttpGet(api."%27%20or%202=1--+","");
        if(GetStrLength(res2[0]) == GetStrLength(res[0])){
            return 1;
        }
    }
    return 0;
}
function track(className,methodName,url){
    array allNode;
    allNode = TrackVarIntoFun(className,methodName,0,"org/springframework/jdbc/core/JdbcTemplate","query",0);
    size = GetArrayNum(allNode);
    if(StrFindStr(GetJavaSentence(allNode[ToInt(size-1)]),".query(",0) != "-1"){
        i = 0;
        print(methodName."白盒测试调用链跟踪:");
        cc = 7;
        cs = 1;
        while(i < size){
            sentence = GetJavaSentence(allNode[i]);
            noSan = filter(sentence);
            if(noSan == 0){cc = 5;cs = 5;}
            if(i == ToInt((size-1))){
                if(cc != 5){cs = 2;cc = 3;}
            }else{}
            if(noSan == 0){
                printcolor("[-]",6);printcolor("想办法绕过此类:",4);
            }else{
                printcolor("[+]",1);
            }
            printcolor(GetClassName(GetNodeClassName(allNode[i]))."   ",cc);
            printcolor(sentence.StrRN(),cs);
            i = ToInt(i+1);
        }
        if(cc != 5){
            printcolor("白盒测试发现此调用链可能存在漏洞,生成测试链接进行黑盒测试".StrRN(),7);
            an = GetClassMethodAnnotation(className,methodName);
            arg_an = GetClassMethodArgAnnotation(className,methodName,0);
            argName = getSpringAnnotationValue(arg_an);
            if(argName != ""){
                api = url.getSpringAnnotationValue(an)."?".argName."=Wker";
                if(judgeSQLI(api) == 1){
                    printcolor("[+]生成测试链接:".api."   测试存在SQL注入漏洞!".StrRN(),3);
                }else{
                    printcolor("[-]生成测试链接:".api."   测试不存在SQL注入漏洞!请自行测试。".StrRN(),5);
                }
            }else{
                printcolor("测试链接生成失败,error:未找到参数入口!".StrRN(),5);
            }
        }
    }
    return 0;
}
function getSpringAnnotationValue(an){
    anSize = GetArrayNum(an);
    i = 0;
    while(i < anSize){
        if(GetAnnotationName(an[i]) == "org/springframework/web/bind/annotation/RequestMapping"){
            allValue = GetAnnotationArgListValue(an[i],"value");
            return allValue[0];
        }
        if(GetAnnotationName(an[i]) == "org/springframework/web/bind/annotation/PostMapping"){
            allValue = GetAnnotationArgListValue(an[i],"value");
            return allValue[0];
        }
        if(GetAnnotationName(an[i]) == "org/springframework/web/bind/annotation/RequestParam"){
            allValue = GetAnnotationArgSingValue(an[i],"value");
            return allValue;
        }

        i = ToInt(i + 1);
    }
    return "";
}
function main(args){
    className = "com/l4yn3/microserviceseclab/controller/IndexController";
    an = GetClassAnnotation(className);
    classPath = "http://127.0.0.1:8080".getSpringAnnotationValue(an);
    methods = GetAllMethodName(className);
    size = GetArrayNum(methods);
    i = 0;
    while(i < size){
        if(methods[i] != "<init>"){track(className,methods[i],classPath);
}
        i = ToInt(i+1);
    }
}

让我们运行一下,看一下最终执行的结果:

wJa丨Java闭源项目的自动化测试

 

可以看到我们最终找到了两处白盒与黑盒完全符合要求的调用链,这样子的调用链是有极大可能存在漏洞的。

以上为今天分享的内容,小伙伴们看懂了吗?

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/155696.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 微信小程序调用支付接口支付失败_微信小程序与后端的接口

    微信小程序调用支付接口支付失败_微信小程序与后端的接口我前段时间做微信支付,遇到了很多坑,网上也没有讲解的特别明白的,通过借鉴各路人才的经验,最后也完成了,网上有很多讲解,我在这只讲一些注意点和解决的方法。我就讲讲我从完全懵到完成的过程吧。在微信提供的接口文档中提供了一个微信支付接口,应该是直接调用这个接口就可以发起微信支付文档路径:https://developers.weixin.qq.com/miniprogram/dev/api/ap…

    2022年9月17日
    3
  • rabbitmq集群搭建_mongodb集群搭建

    rabbitmq集群搭建_mongodb集群搭建先来介绍一下RabbitMQ集群RabbitMQ集群有两种模式,一种是普通模式,即默认的集群模式,另外一种为镜像模式,可以把队列做成镜像队列我们在学习两种模式之前,先来了解下一些关于RabbitMQ集群的概念元数据:包括队列元数据,交换器元数据,交换器元数据,vhost元数据(1)队列元数据:队列名称和它的属性;(2)交换器元数据:交换器名称、类型和属性;(3)绑定元数据:一张简…

    2025年9月5日
    6
  • dom4j和jaxp解析工具的

    dom4j和jaxp解析工具的

    2021年12月4日
    31
  • transition属性详细讲解

    transition属性详细讲解transition属性的格式:transition:transition-property,transition-duration,transition-timing-function,transition-delay;它的四个过渡属性是这样的意思:transition-property:规定设置过渡效果的属性名称。transition-duration:规定完成这个过渡效果需要多少秒或者毫秒。transition-timing-function:规定这个过渡效果的速度曲线。t

    2025年6月9日
    3
  • Spring系列(一) Spring的核心「建议收藏」

    Spring系列(一) Spring的核心「建议收藏」Spring系列(一) Spring的核心

    2022年4月20日
    33
  • 十大免费DNS域名解析服务-稳定,可靠,好用的免费DNS服务

    十大免费DNS域名解析服务-稳定,可靠,好用的免费DNS服务DNS,即DomainNameSystem,中文为域名解析系统,平时我们可能感觉不到DNS的存在,但是一旦DNS域名解析出了问题则可能对网站造成重大损失。就目前来看,DNS可能会因遭到攻击而出现宕机或不能正常解析域名到IP地址。比某某域名DNS解析就经常出现不稳定的情况。而另一个DNS可能存在的问题就是与搜索引擎关系的问题。部分一些DNS可能会因为技术或者人为的因素而屏蔽了搜索引擎的蜘蛛或

    2022年6月22日
    50

发表回复

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

关注全栈程序员社区公众号