静态代码审计工具Cobra/Cobra-W

现有静态代码审计工具

之前童话师傅写过一篇Cobra静态代码审计工具的源码分析,所以先看看这个工具吧。
源码在:
https://github.com/WhaleShark-Team/cobra

后来还有Lo写的一个改版:
https://github.com/LoRexxar/Cobra-W

  • Find Security Bugs
    这个只是提供给IDE的插件。其测试用例可以作为参考。

  • Fortify:不开源。

  • RIPS:只开源了PHP版本,Java的是商业版本。
    参考:https://en.wikipedia.org/wiki/RIPS

  • PMR:
    参考:https://pmd.github.io/
    https://github.com/pmd/pmd
    感觉还比较新,最近一年刚出来。
    不过主要是做编码规范的,不是找漏洞的那种。

  • spotbugs:
    也是编码规范的。

Cobra

跟一下流程:
根据传入的target,若未提供sid,则调用get_sid()新建一个sid。(根据这里的逻辑分支可以看出,如果某次任务退出了,下次可以通过命令行提供这个sid以继续之前的任务?)
在这里插入图片描述

Running(a_sid).status(data)

这一句是把状态信息写入到target对应的文件中。

即便target一样,由于有随机数的存在,所以每次get_sid的结果是不一样的:
在这里插入图片描述
new一个Running实例,然后将状态以字典的形式传入status函数。
这里学习到了如何给正在打开的文件加锁的方式:

import fcntl
fcntl.flock(f, fcntl.LOCK_EX)

参考:https://blog.51cto.com/zhou123/1650185
在这里插入图片描述

最后是关键的开启扫描任务:


cli.start(args.target, args.format, args.output, args.special_rules, a_sid, args.dels)

跟进start函数,
在这里插入图片描述
init_list主要是确定给定的target是list还是普通字符串。一般是普通字符串,于是认为总的目标数为1,初始化这个sid对应的文件,
在这里插入图片描述
然后list就是跟新一下刚才生成的文件。
最后几行是生成report的链接。
在这里插入图片描述

files, file_count, time_consume = Directory(target_directory).collect_files()

用于搜集这个目录下有哪些文件,多少文件,以及花费的时间。
在这里插入图片描述

然后下面是检测(Detection):
主要是通过detection.py来完成的:
在这里插入图片描述
在这里插入图片描述
先加载了cobra下面的一个配置文件:cobra/rules/frameworks.xml,这个文件里面写了一些框架比如ThinkPHP、Jommla、CI等的特征(目录名、文件名等)。
在这里插入图片描述
然后是真正的扫描函数:

scan(target_directory=target_directory, a_sid=a_sid, s_sid=s_sid, special_rules=pa.special_rules,
             language=main_language, framework=main_framework, file_count=file_count, extension_count=len(files))

在这里插入图片描述
跟进去看一下,主要是载入了rules目录下的各种配置文件,包括框架特征的frameworks.xml、语言(后缀名)特征的languages.xml,以及漏洞大类vulnerabilities.xml,以及各种CVI-开头的配置文件,大概长这样:
在这里插入图片描述
其中以CVI-999开头的规则是按年分的CVE漏洞版本规则:
在这里插入图片描述
然后把扫描到的规则文件
在这里插入图片描述

参考

Cobra测试

可测试示例Java代码:

  • https://github.com/JoyChou93/java-sec-code
  • https://github.com/find-sec-bugs/find-sec-bugs/tree/master/findsecbugs-samples-java/src/test/java/testcode

测试一下java-sec-code的代码
命令如下:

 python cobra.py -t /home/cqq/repos/java-sec-code/src/main/java/org/joychou

在这里插入图片描述
结果显示:
总共有21个漏洞,3种漏洞,还有63个规则没有触发,有29个规则关闭了。

看一下对应CVI的id的规则xml文件:
在这里插入图片描述
我们不关心这个堆栈打印信息,于是将开关从on修改为off之后,只显示3个规则了。
在这里插入图片描述

各种CVI对应的漏洞类型

CVI-11:杂/http?http
CVI-12:SSRF(目前只有php的,待加入java的);
CVI-13:一些硬编码的安全风险;
CVI-14:各种XSS;
CVI-15:无此规则;
CVI-16:SQL注入;
CVI-165:LDAP注入;
CVI-167:XXE;
CVI-17:本地文件包含;
CVI-18:RCE;
CVI-19:各种信息泄露;
CVI-20:不安全的随机数(可移除);
CVI-21:url跳转;

CVI-360:大量的webshell检测
在这里插入图片描述

匹配模式

在各个CVI的xml文件中可以看到有匹配模式的差别。有四种模式:

regex-only-match(不区分语言): 默认方式,如果匹配成功,则认为有漏洞;
regex-param-controllable(支持PHP/Java):正则参数可控;
function-param-controllable(仅支持PHP):函数参数可控;
find-extension(寻找某些后缀的文件):匹配到某后缀则认为有漏洞;

详见cobra/const.py

# Match-Mode
mm_find_extension = 'find-extension'
mm_function_param_controllable = 'function-param-controllable'
mm_regex_param_controllable = 'regex-param-controllable'
mm_regex_only_match = 'regex-only-match'
match_modes = [
    mm_regex_only_match,
    mm_regex_param_controllable,
    mm_function_param_controllable,
    mm_find_extension
]

然后在匹配引擎中有具体的判断逻辑(还没看懂)
详见cobra/engine.py
在scan()这个函数中:
在这里插入图片描述
而判断参数是否可控,等的逻辑是在cobra/cast.py中配置的。
CAST(Cross Abstract Syntax Tree)
在这里
有grep命令:

    # 看到最终还是用OS上的grep工具去匹配(怪不得Mac有点问题,可能是跟Mac自带的grep工具有关)
    # -s, --no-messages,表示不显示关于不存在/不可读文件的错误信息
    # -r 表示查找某目录下的所有文件
    # -n 表示显示行号
    # -P 表示使用的是"Perl语言兼容的"" 正则表达式
grep -rnsP "pattern" file_to_grep.java

中间会找到一些行号,然后作为参数传递给sed:
还有sed命令(找到开始行和结束行之间的代码内容):

sed -n 1,3p src/main/java/org/joychou/controller/SSRF.java

在这里插入图片描述

目前了解的,有抽象语法树的,也有正则的。
不过看了一下,已有的规则中,模式是抽象语法树的,主要是php。java的也有但是比较少,暂时还写不出来,先用正则的试试吧。

后来发现cobra的文档里都有:
http://cobra.feei.cn/rule_template
还可以好好看看这个具体的例子:
http://cobra.feei.cn/rule_demo

第一次匹配,
在这里插入图片描述
第一次匹配成功之后进行二次匹配的规则:
在这里插入图片描述

实测发现:
"in-function-down"比“in-current-line”范围大,
比如:
这种代码:

Request.Get(url).execute().returnContent().toString();

两种方式都可以匹配。
但是如果是这种代码:

            req = Request.Get(url);
            return req.execute().returnContent().toString();

就只有"in-function-down"能匹配到了。

in-file-up

比如这个例子CVI-200001.xml
第一次匹配的规则是:

<match mode="regex-only-match"><![CDATA[new Random\s*\(|Random\.next]]></match>

即先匹配到new Random或者Random.next之后,需要确认这个Random确实是java.util.Random或者scala.util.Random这个包里的Random类,于是需要第二次匹配:

<match2 block="in-file-up"><![CDATA[((java|scala)\.util\.Random)]]></match2>

其中in-file-up就表示第一条规则触发的所在行之上所在文件之内,因为这个类只有先引入才能使用,所以通过确认这个类是否被引入,来确认是否是我们关注的那个Random类。

这种方式可以用来匹配静态方法调用的情况。

判断参数是否可控的逻辑在:
cast.py#is_controllable_param
在这里插入图片描述

原来还可以只扫描某两种漏洞:

# 扫描一个文件夹代码的某两种漏洞
$ python cobra.py -t tests/vulnerabilities -r cvi-190001,cvi-190002

漏洞测试用例

  • https://github.com/JoyChou93/java-sec-code
  • https://github.com/find-sec-bugs/find-sec-bugs/blob/master/findsecbugs-samples-java/src/test/java/testcode
  • https://github.com/threedr3am/learnjavabug

SSRF

URLConnection/HttpURLConnection

存在漏洞的代码1

            String url = request.getParameter("url");
            URL u = new URL(url);
            URLConnection urlConnection = u.openConnection();//并不发起请求(只是得到一个对象)
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //发起HTTP请求

存在漏洞的代码2

            String url = request.getParameter("url");
            URL u = new URL(url);
            URLConnection urlConnection = u.openConnection();//并不发起请求(只是得到一个对象)
            HttpURLConnection httpUrl = (HttpURLConnection)urlConnection;
            BufferedReader in = new BufferedReader(new InputStreamReader(httpUrl.getInputStream())); //发起HTTP请求

urlConnection.connect()发起DNS请求;
在这里插入图片描述
urlConnection.getInputStream()发起HTTP请求
在这里插入图片描述
urlConnection.getLastModified();发起HTTP请求
在这里插入图片描述

url.openStream();发起HTTP请求
在这里插入图片描述

url.getContent();发起HTTP请求
在这里插入图片描述

import javax.imageio.ImageIO;
ImageIO.read(u);

在这里插入图片描述

URL跳转

  1. /urlRedirect/redirect?url=http://www.baidu.com
    在这里插入图片描述
    对应代码:
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.GetMapping;

    @GetMapping("/redirect")
    public String redirect(@RequestParam("url") String url) {
        return "redirect:" + url;
    }

检测方式:
"redirect:"字符串,以及包名:org.springframework.web.bind.annotation.RequestParam

  1. /urlRedirect/setHeader?url=http://www.baidu.com
    在这里插入图片描述
    对应代码:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

    @RequestMapping("/setHeader")
    @ResponseBody
    public static void setHeader(HttpServletRequest request, HttpServletResponse response){
        String url = request.getParameter("url");
        response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); // 301 redirect
        response.setHeader("Location", url);
    }

检测方式:
.setHeader("Location"字符串

  1. /urlRedirect/sendRedirect?url=http://www.baidu.com
    在这里插入图片描述
    对应代码:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

    @RequestMapping("/sendRedirect")
    @ResponseBody
    public static void sendRedirect(HttpServletRequest request, HttpServletResponse response) throws IOException{
        String url = request.getParameter("url");
        response.sendRedirect(url); // 302 redirect
    }

//TODO

发布了601 篇原创文章 · 获赞 101 · 访问量 100万+

猜你喜欢

转载自blog.csdn.net/caiqiiqi/article/details/104158061