返回列表 发新帖

[防攻] 基于机器学习的jsp/jspx webshell检测

[复制链接]
灌水成绩
971
主题
992
帖子
5892
积分
等级头衔
积分成就
  • 威望: 0
  • 贡献: 4900
  • 金钱: 0
  • 违规:
  • 在线时间:336 小时
  • 注册时间:2019-3-9
  • 最后登录:2020-1-27
个人勋章

官方

联系方式
QQ
发表在  2019-8-21 22:30:21 | 显示全部楼层 | 阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册

x
前言
在上文《基于AST的Webshell检测》中,笔者已经提出了基于抽象语法树是可以用来检测Webshell的,那么如何将这种思想应用在jsp/jspx的webshell检测便是本文讨论的重点。
简单回顾下抽象语法树的检测原来,由于webshell和正常文件在语法结构上会有比较明显的出入,比如说一句话木马普遍流程就是传参然后执行命令,转化为语法结构上其实是比较单一的,正常文件的语法结构会比这复杂的多得多,因此从语法结构上来分辨是否为webshell也是一种不错的选择。但是语法结构的缺点就是难以对具体参数进行分析,所以当出现“eval('1111');”和“eval(file_put_contents('shell.php','<?php phpinfo()'));”这种需要去区分具体数据的时候使用ast来做检测就会显得非常困难。
那么应用到java环境下,黑客的正常攻击流程就是通过web漏洞传入webshell,然后通过小马传大马的方式进行后渗透攻击,一般java环境下想要rce要么任意文件上传,要么反序列化执行命令(还有其他方法,这里只列举主流方法),所以通过任意文件上传就一定会留下文件痕迹,一般来说java的可执行文件为jsp或者jspx,一般情况下jspx是一种绕过jsp文件上传的方式,这里作一并处理。
回顾前面的检测原理,jsp文件的语法结构特征其实相较于php来说,是更为明显的。原因在于一般情况下jsp文件是作页面展示用的,而具有webshell特征的jsp文件是通过传参的方式来执行命令,在语法结构上其实是有本质的区别,所以这里起码检测原理是说的过去的,并且切实可行的。
数据处理
机器学习我们知道最重要的一步其实就是特征工程的构建上,那么这里jsp文件其实是没有专用的工具来解析其抽象语法树的,并且jsp文件跟php文件的最大区别就是php文件时可以直接执行的,而jsp文件其实是需要通过中间件进行编译后才能执行,那么这个问题其实就转化为了:在Tomcat等中间件服务器下,jsp文件经过了哪些处理流程?
这里jsp文件首先经过jsp parse会被编译为java文件,然后通过servlet分析器将java文件编译为class文件,最后将其转化为对应的Java字节码加载执行,这中间其实有一步比较关键,那就是编译为java文件这一步。
文件编译
那么作为检测程序来说,肯定不能说让tomcat调用这程序一遍,然后直接抓编译好的java文件,那么这里就必须要先运行tomcat服务器,使其自动加载WEB目录下的程序,这对于我们自动化检测来说肯定是不可取的,如果现在想要批量检测,就必须要有自己的编译工具,所幸这里找到了tomcat中编译jsp程序的类(org.apache.jasper.servlet.JspServlet),这里其实是一个比较大的坑,这里将编译命令直接放出来。
[size=0.9em]ActionScript

java -cp "apache-tomcat-8.5.35/lib/*:apache-tomcat-8.5.35/bin/*" org.apache.jasper.JspC -webapp  webroot -d webroot_java #直接运行可能会有点问题,需要在lib目录下加入ant.jar包#-webapp : 指定文件目录#-d : 指定编译后的文件目录

通过运行上述命令,我们其实就已经可以将某一个存放webshell文件的目录给转化为java文件,其中发现对于jspx文件的编译也是使用了同样的模块,所以这里也能够检测jspx的webshell。
抽象语法树的构建
针对jsp的抽象语法树构建笔者并没有找到相关工具,但是针对java的ast构建工具起码还有选择,这里使用的是javaparser这个工具来生成相应的抽象语法树
使用javaparser也比较简单,通过maven架构直接加载对应的pom信息即可
<dependencies>
    <dependency>
        <groupId>com.github.javaparser</groupId>
        <artifactId>javaparser-core</artifactId>
        <version>3.14.8</version>
    </dependency></dependencies>
这里在参照了网上的大部分教程后,决定使用文件流的方式来静态编译。
public static void main(String[] args) throws Exception {
    String filename = args[0];
    //File file = new File("src/classes/org/apache/jsp/s03_jsp.java");
    File file = new File(filename);
    FileInputStream in = new FileInputStream(file);

    CompilationUnit cu = StaticJavaParser.parse(in);
    cu.accept(new MethodVistor(),null);}
这里的MethodVistor类其实就是语法结构类型的检测方法,比如说函数调用可能就叫MethodCall,如果是注释就叫Comment,所以说经过这个类我们就能够生成全局的语法结构节点序列。这中间并不是取了所有的语法结构特征,并且针对部分语法结构特征做了深一步处理,如函数调用可能需要进一步获取函数名等。
机器学习
得到这个序列后,需要使用相关模型来将其转化为矩阵,以便后面的训练和学习,针对这种序列流模型,我采用的是tfidf模型,主要思想就是如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。这个模型其实是由词频和逆向文件频率组成的,最后表示其实就是两个参数的乘积,这个不多说,,没什么意义。。
CV = CountVectorizer(ngram_range=(1,3), decode_error="ignore",max_features=max_features,
                                       token_pattern = r'\b\w+\b',min_df=0,max_df=0.9)
    x=CV.fit_transform(x_all).toarray()
    transformer = TfidfTransformer(smooth_idf=False)
    x_tfidf = transformer.fit_transform(x)
    x = x_tfidf.toarray()
那么通过这个模型,我们就能将每个文件的ast语法结构序列给转化为一个统一的矩阵,并分别给黑白样本打上标记,进行有监督式的训练。这里黑样本来源于github上的开源仓库,白样本的获取其实有点难度,这里也是搜寻了大量的开源cms,不过白样本依然很少,原因比较简单,一个cms的jsp文件毕竟有限,所以这里唯一比较遗憾的就是数据量的问题。其中黑样本数量为632,白样本数量为470。
最后选取算法,这里参照前文的检测经验,初步选定了xgboost、随机森林、mlp等三种算法,最后经过漫长的调参和比较后,裁定各个算法的最优参数。
实验结果
采用随机算法的检测结果
采用xgboost算发的检测结果
采用mlp算法的检测结果
后记
回头看来,觉得整个实现思路上还是比较简单的,就是可能有几个坑点的确比较烦一点,不过感觉本文只能作为检测jsp/jspx webshell的基本思路,复杂点的还是会被绕过,如果真的想要提高检测精度,自我感觉对参数语义的检测还是非常有必要的!
上述如有不当之处,敬请指出~

温馨提示:
1、在论坛里发表的文章仅代表作者本人的观点,与本网站立场无关。
2、论坛的所有内容都不保证其准确性,有效性,时间性。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
3、当政府机关依照法定程序要求披露信息时,论坛均得免责。
4、若因线路及非本站所能控制范围的故障导致暂停服务期间造成的一切不便与损失,论坛不负任何责任。
5、注册会员通过任何手段和方法针对论坛进行破坏,我们有权对其行为作出处理。并保留进一步追究其责任的权利。
6.添加QQ群随时获取新消息QQ群号:831240632
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表