freeBuf
semgrep 分析log4j漏洞
2022-02-12 12:07:50
所属地 上海

前言

看到很多师傅都是使用codeql 来找log4j2 shell 的Sink和Source,例如FreeBuf 上的文章《codeql分析log4j》 、先知社区《利用CodeQL分析并挖掘Log4j漏洞》 。由于最近一直在捣鼓semgrep,恰又没看到有人使用semgrep 来分析log4j的,因此就来尝试使用semgrep看看能否很容易地找到Sink和Source。其中注意到semgrep 网站也有针对log4j2 shell 漏洞检测规则 了,规则也都比较基础,适合初学者,感兴趣的可以阅读 下。

img

环境搭建

相比codeql 需要对log4j源码进行编译生成代码数据库的过程,semgrep 不需要对需要分析的代码进行编译的,因此直接把logging-log4j2-rel-2.14.1 这个版本的源码下载到本地即可,非常简单。

这里我使用semgrep docker 方式部署来进行代码扫描的:

docker run --rm -v "${PWD}:/src" returntocorp/semgrep --config=s/xx  ./  -o scan-c.txt  --time --debug

关于semgrep 的更多详细使用方式可参考我之前的文章 semgrep SAST编写自定义漏洞检测规则实践

编写规则

当然Sink 点还是落在JNDI注入的lookup方法上,所以首先初步匹配下lookup 方法,关键pattern: $NAME.lookup(...);查找调用了lookup()方法的代码。

1646030088_621c6d08b92e5d671bc0c.png!small?1646030102271

在调试阶段,我们可以在https://semgrep.dev/editor上编写规则,验证好了之后可以通过share方式,然后通过–config= 去加载指定的规则,这样比较方便快捷,线上验证语法没问题就可以跑本地代码进行测试了。

imgimg从上图可以看到,找到了16处存在调用lookup方法的代码行位置:img

优化规则

上一步的规则找到了16处的问题,但是一个一个去分析有点多,有必要进一步筛选下。知道JNDI注入会使用JNDI context方式,其中应该都会引入javax.naming.Context 或者 javax.naming.InitialContext 类,而且InitialContext 实现了Context 接口。 因此继续改造下规则,这次我们引入patterns 及pattern-inside 关键字来进行限定,用两个patterns 代表下述的两种情况,每个patterns 都有一个pattern-inside 和一个查找lookup 的pattern 组成。

  • (1) 要求调用lookup方法前导入了 javax.naming.Context
  • (2) 要求调用lookup方法前导入了 javax.naming.InitialContext

基于上面的两种情况,编写如下规则:

1646030479_621c6e8f8b4a656864a83.png!small?1646030493087

只需片刻时间,检测结果就出来了,这次看到只找到两处。img

分别为 DataSourceConnectionSource#createConnectionSource和 JndiManager#lookupimg通过上述规则找到了Sink点。

  • JndiManager#lookup
    @SuppressWarnings("unchecked")
      public <T> T lookup(final String name) throws NamingException {
          return (T) this.context.lookup(name);
      }
    
  • DataSourceConnectionSource#createConnectionSource
    @PluginFactory
      public static DataSourceConnectionSource createConnectionSource(@PluginAttribute("jndiName") final String jndiName) {
          if (Strings.isEmpty(jndiName)) {
              LOGGER.error("No JNDI name provided.");
              return null;
          }
    
          try {
              final InitialContext context = new InitialContext();
              final DataSource dataSource = (DataSource) context.lookup(jndiName);
              if (dataSource == null) {
                  LOGGER.error("No data source found with JNDI name [" + jndiName + "].");
                  return null;
              }
    
              return new DataSourceConnectionSource(jndiName, dataSource);
          } catch (final NamingException e) {
              LOGGER.error(e.getMessage(), e);
              return null;
          }
      }
    

关于查找Source

首先,semgrep 对于数据流分析能力相较Codeql 来比是很弱的,因此想找到一条完整的调用链是非常困难。它最大的魅力在于已知Source和Sink或者sanitized的情况下,用于自动化检查开发是否按照编码规范进行编码的工具。

本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
文章目录